From 43f3b9fc675b68ec1d55f61ab889adb2164c6963 Mon Sep 17 00:00:00 2001 From: sujan Date: Fri, 26 Apr 2024 17:09:46 +0545 Subject: [PATCH 1/2] fix: actual number of contributors in project summary --- src/backend/app/projects/project_crud.py | 25 +++++---- src/backend/app/projects/project_routes.py | 4 +- src/backend/app/projects/project_schemas.py | 55 +++++-------------- src/frontend/src/api/Project.js | 1 - .../src/models/project/projectModel.ts | 1 - 5 files changed, 32 insertions(+), 54 deletions(-) diff --git a/src/backend/app/projects/project_crud.py b/src/backend/app/projects/project_crud.py index f793a5d428..a3c708920b 100644 --- a/src/backend/app/projects/project_crud.py +++ b/src/backend/app/projects/project_crud.py @@ -187,7 +187,7 @@ async def get_project_summaries( project_count, db_projects = await get_projects( db, skip, limit, user_id, hashtags, search ) - return project_count, await convert_to_project_summaries(db_projects) + return project_count, await convert_to_project_summaries(db, db_projects) async def get_project(db: Session, project_id: int): @@ -1265,7 +1265,7 @@ async def convert_project(project): return [] -async def convert_to_project_summary(db_project: db_models.DbProject): +async def convert_to_project_summary(db: Session, db_project: db_models.DbProject): """Legacy function to convert db models --> Pydantic. TODO refactor to use Pydantic model methods instead. @@ -1274,13 +1274,17 @@ async def convert_to_project_summary(db_project: db_models.DbProject): summary: project_schemas.ProjectSummary = db_project if db_project.project_info: - summary.location_str = db_project.location_str summary.title = db_project.project_info.name summary.description = db_project.project_info.short_description - summary.num_contributors = ( - db_project.tasks_mapped + db_project.tasks_validated - ) # TODO: get real number of contributors + db.query(db_models.DbTaskHistory.user_id) + .filter( + db_models.DbTaskHistory.project_id == db_project.id, + db_models.DbTaskHistory.user_id!=(None), + ) + .distinct() + .count() + ) summary.organisation_logo = ( db_project.organisation.logo if db_project.organisation else None ) @@ -1291,7 +1295,8 @@ async def convert_to_project_summary(db_project: db_models.DbProject): async def convert_to_project_summaries( - db_projects: List[db_models.DbProject], + db: Session, + db_projects: List[db_models.DbProject], ) -> List[project_schemas.ProjectSummary]: """Legacy function to convert db models --> Pydantic. @@ -1299,11 +1304,11 @@ async def convert_to_project_summaries( """ if db_projects and len(db_projects) > 0: - async def convert_summary(project): - return await convert_to_project_summary(project) + async def convert_summary(db, project): + return await convert_to_project_summary(db, project) project_summaries = await gather( - *[convert_summary(project) for project in db_projects] + *[convert_summary(db, project) for project in db_projects] ) return [summary for summary in project_summaries if summary is not None] else: diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index b92f0a0ecf..2e86a72d5a 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -141,7 +141,7 @@ async def read_project_summaries( ) project_summaries = [ - project_schemas.ProjectSummary.from_db_project(project) for project in projects + project_schemas.ProjectSummary(**project.__dict__) for project in projects ] response = project_schemas.PaginatedProjectSummaries( @@ -181,7 +181,7 @@ async def search_project( page, project_count, results_per_page, total_projects ) project_summaries = [ - project_schemas.ProjectSummary.from_db_project(project) for project in projects + project_schemas.ProjectSummary(**project.__dict__) for project in projects ] response = project_schemas.PaginatedProjectSummaries( diff --git a/src/backend/app/projects/project_schemas.py b/src/backend/app/projects/project_schemas.py index 40ecc1793e..f8b75c0db4 100644 --- a/src/backend/app/projects/project_schemas.py +++ b/src/backend/app/projects/project_schemas.py @@ -22,6 +22,7 @@ from typing import Any, List, Optional, Union from dateutil import parser +from geoalchemy2.elements import WKBElement from geojson_pydantic import Feature, FeatureCollection, Polygon from loguru import logger as log from pydantic import BaseModel, Field, computed_field @@ -241,11 +242,10 @@ class GeojsonFeature(BaseModel): class ProjectSummary(BaseModel): """Project summaries.""" - id: int = -1 - priority: ProjectPriority = ProjectPriority.MEDIUM - priority_str: str = priority.name + id: int + priority: ProjectPriority title: Optional[str] = None - centroid: Optional[list[float]] = None + centroid: Optional[WKBElement]= None location_str: Optional[str] = None description: Optional[str] = None total_tasks: Optional[int] = None @@ -257,42 +257,17 @@ class ProjectSummary(BaseModel): organisation_id: Optional[int] = None organisation_logo: Optional[str] = None - @classmethod - def from_db_project( - cls, - project: db_models.DbProject, - ) -> "ProjectSummary": - """Generate model from database obj.""" - priority = project.priority - centroid_coords = [] - if project.centroid: - centroid_point = read_wkb(project.centroid) - # NOTE format x,y (lon,lat) required for GeoJSON - centroid_coords = [centroid_point.x, centroid_point.y] - - return cls( - id=project.id, - priority=priority, - priority_str=priority.name, - title=project.title, - centroid=centroid_coords, - location_str=project.location_str, - description=project.description, - total_tasks=project.total_tasks, - tasks_mapped=project.tasks_mapped, - num_contributors=project.num_contributors, - tasks_validated=project.tasks_validated, - tasks_bad=project.tasks_bad, - hashtags=project.hashtags, - organisation_id=project.organisation_id, - organisation_logo=project.organisation_logo, - ) - - # @field_serializer("centroid") - # def get_coord_from_centroid(self, value): - # """Get the cetroid coordinates from WBKElement.""" - # if value is None: - # return None + class Config: + arbitrary_types_allowed = True + + @field_serializer("centroid") + def get_coord_from_centroid(self, value): + """Get the cetroid coordinates from WBKElement.""" + if value is None: + return None + centroid_point = read_wkb(value) + centroid = [centroid_point.x, centroid_point.y] + return centroid class PaginationInfo(BaseModel): diff --git a/src/frontend/src/api/Project.js b/src/frontend/src/api/Project.js index 98b9af375b..aae38ff8d3 100755 --- a/src/frontend/src/api/Project.js +++ b/src/frontend/src/api/Project.js @@ -31,7 +31,6 @@ export const ProjectById = (existingProjectList, projectId) => { id: projectResp.id, outline_geojson: projectResp.outline_geojson, priority: projectResp.priority || 2, - priority_str: projectResp.priority_str || 'MEDIUM', title: projectResp.project_info?.name, location_str: projectResp.location_str, description: projectResp.project_info?.description, diff --git a/src/frontend/src/models/project/projectModel.ts b/src/frontend/src/models/project/projectModel.ts index 9cb3eb864d..e8848655d6 100644 --- a/src/frontend/src/models/project/projectModel.ts +++ b/src/frontend/src/models/project/projectModel.ts @@ -54,7 +54,6 @@ export type projectInfoType = { id: number; }; priority: number; - priority_str: string; title: string; location_str: string; description: string; From 05f94896a1a101128811001ef8728dcb145382d5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 11:27:40 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/backend/app/projects/project_crud.py | 6 +++--- src/backend/app/projects/project_schemas.py | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/backend/app/projects/project_crud.py b/src/backend/app/projects/project_crud.py index a3c708920b..a459de7fa8 100644 --- a/src/backend/app/projects/project_crud.py +++ b/src/backend/app/projects/project_crud.py @@ -1280,7 +1280,7 @@ async def convert_to_project_summary(db: Session, db_project: db_models.DbProjec db.query(db_models.DbTaskHistory.user_id) .filter( db_models.DbTaskHistory.project_id == db_project.id, - db_models.DbTaskHistory.user_id!=(None), + db_models.DbTaskHistory.user_id != (None), ) .distinct() .count() @@ -1295,8 +1295,8 @@ async def convert_to_project_summary(db: Session, db_project: db_models.DbProjec async def convert_to_project_summaries( - db: Session, - db_projects: List[db_models.DbProject], + db: Session, + db_projects: List[db_models.DbProject], ) -> List[project_schemas.ProjectSummary]: """Legacy function to convert db models --> Pydantic. diff --git a/src/backend/app/projects/project_schemas.py b/src/backend/app/projects/project_schemas.py index f8b75c0db4..5b677e4d3b 100644 --- a/src/backend/app/projects/project_schemas.py +++ b/src/backend/app/projects/project_schemas.py @@ -32,7 +32,6 @@ from typing_extensions import Self from app.config import HttpUrlStr, decrypt_value, encrypt_value -from app.db import db_models from app.db.postgis_utils import ( geojson_to_geometry, geometry_to_geojson, @@ -245,7 +244,7 @@ class ProjectSummary(BaseModel): id: int priority: ProjectPriority title: Optional[str] = None - centroid: Optional[WKBElement]= None + centroid: Optional[WKBElement] = None location_str: Optional[str] = None description: Optional[str] = None total_tasks: Optional[int] = None @@ -259,7 +258,7 @@ class ProjectSummary(BaseModel): class Config: arbitrary_types_allowed = True - + @field_serializer("centroid") def get_coord_from_centroid(self, value): """Get the cetroid coordinates from WBKElement."""