Skip to content

Commit

Permalink
Merge pull request #6608 from hotosm/fastapi-refactor
Browse files Browse the repository at this point in the history
Messages, teams, comments, notification modules and project filters a…
  • Loading branch information
prabinoid authored Oct 29, 2024
2 parents ecb376a + dd04469 commit c4361e2
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 196 deletions.
47 changes: 27 additions & 20 deletions backend/api/comments/resources.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
# from flask_restful import , request, current_app
# from schematics.exceptions import Exception

from datetime import datetime

from databases import Database
from fastapi import APIRouter, Depends, Request
from loguru import logger
from starlette.authentication import requires
from fastapi.responses import JSONResponse

from backend.db import get_db, get_session
Expand All @@ -16,7 +12,7 @@
from backend.services.mapping_service import MappingService, MappingServiceError
from backend.services.messaging.chat_service import ChatService
from backend.services.project_service import ProjectService
from backend.services.users.authentication_service import login_required, tm
from backend.services.users.authentication_service import login_required
from backend.services.users.user_service import UserService

session = get_session()
Expand Down Expand Up @@ -204,9 +200,15 @@ async def delete(


@router.post("/{project_id}/comments/tasks/{task_id}/")
@requires("authenticated")
@tm.pm_only(False)
def post(request: Request, project_id: int, task_id: int):
# TODO Decorator
# @tm.pm_only(False)
async def post(
request: Request,
project_id: int,
task_id: int,
user: AuthUserDTO = Depends(login_required),
db: Database = Depends(get_db),
):
"""
Adds a comment to the task outside of mapping/validation
---
Expand Down Expand Up @@ -260,24 +262,29 @@ def post(request: Request, project_id: int, task_id: int):
description: Internal Server Error
"""
authenticated_user_id = request.user.display_name
if UserService.is_user_blocked(authenticated_user_id):
return {"Error": "User is on read only mode", "SubCode": "ReadOnly"}, 403
if await UserService.is_user_blocked(authenticated_user_id, db):
return JSONResponse(
content={"Error": "User is on read only mode", "SubCode": "ReadOnly"},
status_code=403,
)

try:
task_comment = TaskCommentDTO(request.json())
task_comment.user_id = request.user.display_name
task_comment.task_id = task_id
task_comment.project_id = project_id
task_comment.validate()
request_json = await request.json()
comment = request_json.get("comment")
task_comment = TaskCommentDTO(
user_id=user.id, task_id=task_id, project_id=project_id, comment=comment
)
except Exception as e:
logger.error(f"Error validating request: {str(e)}")
return {"Error": "Unable to add comment", "SubCode": "InvalidData"}, 400

return JSONResponse(
content={"Error": "Unable to add comment", "SubCode": "InvalidData"},
status_code=400,
)
try:
task = MappingService.add_task_comment(task_comment)
return task.model_dump(by_alias=True), 201
task = await MappingService.add_task_comment(task_comment, db)
return task
except MappingServiceError:
return {"Error": "Task update failed"}, 403
return JSONResponse(content={"Error": "Task update failed"}, status_code=403)


@router.get("/{project_id}/comments/tasks/{task_id}/")
Expand Down
2 changes: 1 addition & 1 deletion backend/api/projects/favorites.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ async def get(
favorited = await ProjectService.is_favorited(project_id, user_id, db)
if favorited is True:
return JSONResponse(content={"favorited": True}, status_code=200)
return JSONResponse({"favorited": False}, status_code=200)
return JSONResponse(content={"favorited": False}, status_code=200)


@router.post("/{project_id}/favorite/")
Expand Down
10 changes: 7 additions & 3 deletions backend/api/teams/resources.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from backend.models.postgis.team import Team
from databases import Database
from distutils.util import strtobool
from fastapi import APIRouter, Depends, Request, Body
Expand Down Expand Up @@ -94,7 +95,7 @@ async def patch(
try:
team = await TeamService.get_team_by_id(team_id, db)
team_dto.team_id = team_id

data = await request.json()
if not await TeamService.is_user_team_manager(
team_id, user.id, db
) and not await OrganisationService.can_user_manage_organisation(
Expand All @@ -112,9 +113,12 @@ async def patch(
return JSONResponse(
content={"Error": str(e), "SubCode": "InvalidData"}, status_code=400
)

try:
await TeamService.update_team(team_dto, db)
if ("joinMethod" or "organisations_id") not in data.keys():
print("inside......")
await Team.update_team_members(team, team_dto, db)
else:
await TeamService.update_team(team_dto, db)
return JSONResponse(content={"Status": "Updated"}, status_code=200)
except TeamServiceError as e:
return JSONResponse(content={"Error": str(e)}, status_code=402)
Expand Down
4 changes: 3 additions & 1 deletion backend/api/users/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,9 @@ async def get(
page = (
int(request.query_params.get("page")) if request.query_params.get("page") else 1
)
project_id = int(request.query_params.get("projectId", None))
project_id = request.query_params.get("projectId", None)
if project_id:
project_id = int(project_id)
users_dto = await UserService.filter_users(username, project_id, page, db)
return users_dto

Expand Down
25 changes: 14 additions & 11 deletions backend/models/dtos/team_dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ class TeamMembersDTO(BaseModel):
def validate_function(cls, value):
return validate_team_member_function(value)

class Config:
populate_by_name = True


class TeamProjectDTO(BaseModel):
"""Describes a JSON model to create a project team"""
Expand Down Expand Up @@ -108,7 +111,7 @@ class TeamDetailsDTO(BaseModel):
visibility: str
is_org_admin: bool = Field(False)
is_general_admin: bool = Field(False)
members: List[TeamMembersDTO] = Field([], alias="team_members")
members: List[TeamMembersDTO] = Field([], alias="members")
team_projects: List[TeamProjectDTO] = Field([], alias="team_projects")

@field_validator("join_method")
Expand Down Expand Up @@ -200,16 +203,16 @@ class Config:
class UpdateTeamDTO(BaseModel):
"""Describes a JSON model to update a team"""

creator: float = Field(None, alias="creator")
team_id: int = Field(None, alias="team_id")
organisation: str = Field(None, alias="organisation")
organisation_id: int = Field(None, alias="organisation_id")
name: str = Field(None, alias="name")
logo: str = Field(None, alias="logo")
description: str = Field(None, alias="description")
join_method: str = Field(None, alias="joinMethod")
visibility: str = Field(None, serialize_when_none=False)
members: List[TeamMembersDTO] = Field([], serialize_when_none=False)
creator: Optional[int] = Field(None, alias="creator")
team_id: Optional[int] = Field(None, alias="team_id")
organisation: Optional[str] = Field(None, alias="organisation")
organisation_id: Optional[int] = Field(None, alias="organisation_id")
name: Optional[str] = Field(None, alias="name")
logo: Optional[str] = Field(None, alias="logo")
description: Optional[str] = Field(None, alias="description")
join_method: Optional[str] = Field(None, alias="joinMethod")
visibility: Optional[str] = Field(None, serialize_when_none=False)
members: Optional[List[TeamMembersDTO]] = Field([], serialize_when_none=False)

@field_validator("join_method")
def validate_join_method(cls, value):
Expand Down
43 changes: 24 additions & 19 deletions backend/models/postgis/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,27 @@ async def save(self, db: Database):
)

@staticmethod
def get_all_contributors(project_id: int):
"""Get all contributors to a project"""

contributors = (
session.query(Task.mapped_by)
.filter(Task.project_id == project_id)
.filter(Task.mapped_by.isnot(None))
.union(
session.query(Task.validated_by)
.filter(Task.project_id == project_id)
.filter(Task.validated_by.isnot(None))
)
.distinct()
).all()
async def get_all_contributors(project_id: int, db: Database):
"""Get all contributors to a project using async raw SQL"""

query = """
SELECT DISTINCT contributor
FROM (
SELECT mapped_by AS contributor
FROM tasks
WHERE project_id = :project_id
AND mapped_by IS NOT NULL
UNION
SELECT validated_by AS contributor
FROM tasks
WHERE project_id = :project_id
AND validated_by IS NOT NULL
) AS contributors
"""

rows = await db.fetch_all(query=query, values={"project_id": project_id})

contributors = [row["contributor"] for row in rows]
return contributors

@staticmethod
Expand Down Expand Up @@ -222,14 +229,12 @@ async def delete_all_messages(
DELETE FROM messages
WHERE to_user_id = :user_id
"""
params = {"user_id": user_id}

if message_type_filters:
delete_query += " AND message_type = ANY(:message_type_filters)"

await db.execute(
delete_query,
{"user_id": user_id, "message_type_filters": message_type_filters},
)
params["message_type_filters"] = message_type_filters
await db.execute(delete_query, params)

def delete(self):
"""Deletes the current model from the DB"""
Expand Down
Loading

0 comments on commit c4361e2

Please sign in to comment.