Skip to content

Commit

Permalink
Merge pull request #6660 from hotosm/fastapi-refactor
Browse files Browse the repository at this point in the history
* Export projects as csv * Project partnership filters * Changeset comment population on project creation and update * Cleanups
  • Loading branch information
prabinoid authored Dec 17, 2024
2 parents c34a95f + 1cb3e76 commit 556bb3f
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 62 deletions.
61 changes: 61 additions & 0 deletions backend/api/projects/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ProjectSearchDTO,
)
from backend.models.dtos.user_dto import AuthUserDTO
from backend.models.postgis.statuses import UserRole
from backend.services.organisation_service import OrganisationService
from backend.services.project_admin_service import (
InvalidData,
Expand Down Expand Up @@ -545,6 +546,10 @@ def setup_search_dto(request) -> ProjectSearchDTO:
search_dto.last_updated_lte = request.query_params.get("lastUpdatedTo")
search_dto.created_gte = request.query_params.get("createdFrom")
search_dto.created_lte = request.query_params.get("createdTo")
search_dto.partner_id = request.query_params.get("partnerId")
search_dto.partnership_from = request.query_params.get("partnershipFrom")
search_dto.partnership_to = request.query_params.get("partnershipTo")
search_dto.download_as_csv = request.query_params.get("downloadAsCSV")

# See https://github.com/hotosm/tasking-manager/pull/922 for more info
try:
Expand All @@ -565,6 +570,7 @@ def setup_search_dto(request) -> ProjectSearchDTO:

if request.query_params.get("managedByMe") == "true":
search_dto.managed_by = authenticated_user_id

if request.query_params.get("basedOnMyInterests") == "true":
search_dto.based_on_user_interests = authenticated_user_id

Expand Down Expand Up @@ -740,6 +746,61 @@ async def get(
if user_id:
user = await UserService.get_user_by_id(user_id, db)
search_dto = setup_search_dto(request)

if search_dto.omit_map_results and search_dto.download_as_csv:
return JSONResponse(
content={
"Error": "omitMapResults and downloadAsCSV cannot be both set to true"
},
status_code=400,
)

if (
search_dto.partnership_from is not None
or search_dto.partnership_to is not None
) and search_dto.partner_id is None:
return JSONResponse(
content={
"Error": "partnershipFrom or partnershipTo cannot be provided without partnerId"
},
status_code=400,
)

if (
search_dto.partner_id is not None
and search_dto.partnership_from is not None
and search_dto.partnership_to is not None
and search_dto.partnership_from > search_dto.partnership_to
):
return JSONResponse(
content={
"Error": "partnershipFrom cannot be greater than partnershipTo"
},
status_code=400,
)

if any(
map(
lambda x: x is not None,
[
search_dto.partner_id,
search_dto.partnership_from,
search_dto.partnership_to,
],
)
) and (user is None or not user.role == UserRole.ADMIN.value):
error_msg = "Only admins can search projects by partnerId, partnershipFrom, partnershipTo"
return JSONResponse(content={"Error": error_msg}, status_code=401)

if search_dto.download_as_csv:
all_results_csv = await ProjectSearchService.search_projects_as_csv(
search_dto, user, db, True
)
return StreamingResponse(
iter([all_results_csv]),
media_type="text/csv",
headers={"Content-Disposition": "attachment; filename=data.csv"},
)
results_dto = await ProjectSearchService.search_projects(search_dto, user, db)
return results_dto
except NotFound:
Expand Down
12 changes: 11 additions & 1 deletion backend/models/dtos/project_dto.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import date, datetime
from typing import Dict, List, Optional, Union, Any
from typing import Any, Dict, List, Optional, Union

from fastapi import HTTPException
from pydantic import BaseModel, Field, root_validator
Expand Down Expand Up @@ -368,6 +368,10 @@ class ProjectSearchDTO(BaseModel):
last_updated_gte: Optional[str] = None
created_lte: Optional[str] = None
created_gte: Optional[str] = None
partner_id: Optional[int] = None
partnership_from: Optional[str] = None
partnership_to: Optional[str] = None
download_as_csv: Optional[bool] = None

def __hash__(self):
"""Make object hashable so we can cache user searches"""
Expand Down Expand Up @@ -441,6 +445,12 @@ class ListSearchResultDTO(BaseModel):
total_contributors: Optional[int] = Field(alias="totalContributors", default=None)
country: Optional[List[str]] = Field(default=None)

# csv fields
creation_date: Optional[datetime] = Field(alias="creationDate", default=None)
author: Optional[str] = None
partner_names: Optional[List[str]] = Field(default=None, alias="partnerNames")
total_area: Optional[float] = Field(None, alias="totalAreaSquareKilometers")

class Config:
populate_by_name = True

Expand Down
19 changes: 15 additions & 4 deletions backend/models/postgis/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
from geoalchemy2.shape import to_shape
from loguru import logger
from shapely.geometry import shape
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.hybrid import hybrid_property


from sqlalchemy import (
BigInteger,
Boolean,
Expand All @@ -33,6 +29,8 @@
select,
update,
)
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import backref, relationship

from backend.config import settings
Expand Down Expand Up @@ -358,6 +356,19 @@ async def create(self, project_name: str, db: Database):
project_id=project, locale="en", name=project_name
)
)
# Set the default changeset comment
default_comment = settings.DEFAULT_CHANGESET_COMMENT
self.changeset_comment = (
f"{default_comment}-{project} {self.changeset_comment}"
if self.changeset_comment is not None
else f"{default_comment}-{project}"
)
# Update the changeset comment in the database
await db.execute(
Project.__table__.update()
.where(Project.__table__.c.id == project)
.values(changeset_comment=self.changeset_comment)
)

for task in self.tasks:
await db.execute(
Expand Down
5 changes: 2 additions & 3 deletions backend/services/project_admin_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,16 @@ async def create_draft_project(
tasks = draft_project_dto.tasks

await ProjectAdminService._attach_tasks_to_project(draft_project, tasks, db)
draft_project.set_default_changeset_comment()
draft_project.set_country_info()

if draft_project_dto.cloneFromProjectId:
draft_project.set_default_changeset_comment()
await draft_project.save(db) # Update the clone
return draft_project.id

else:
project_id = await Project.create(
draft_project, draft_project_dto.project_name, db
) # Create the new project

return project_id

@staticmethod
Expand Down
Loading

0 comments on commit 556bb3f

Please sign in to comment.