diff --git a/src/backend/app/projects/project_crud.py b/src/backend/app/projects/project_crud.py index aa261ea629..0f4c6c7438 100644 --- a/src/backend/app/projects/project_crud.py +++ b/src/backend/app/projects/project_crud.py @@ -69,6 +69,9 @@ from app.projects import project_schemas from app.tasks import tasks_crud from app.users import user_crud +from app.submission import submission_crud +from app.s3 import get_obj_from_bucket +from app.organization import organization_crud QR_CODES_DIR = "QR_codes/" TASK_GEOJSON_DIR = "geojson/" @@ -2385,3 +2388,27 @@ async def get_pagination(page: int, count: int, results_per_page: int, total: in ) return pagination + + +async def get_dashboard_detail(project_id: int, db: Session): + """Get project details for project dashboard.""" + + project = await get_project(db, project_id) + db_organization = await organization_crud.get_organisation_by_id(db, project.organisation_id) + + s3_project_path = f"/{project.organisation_id}/{project_id}" + s3_submission_path = f"/{s3_project_path}/submissions.meta.json" + + file = get_obj_from_bucket(settings.S3_BUCKET_NAME, s3_submission_path) + project.last_active = (json.loads(file.getvalue()))["last_submission"] + + contributors = db.query(db_models.DbTaskHistory).filter(db_models.DbTaskHistory.project_id==project_id).all() + unique_user_ids = {user.user_id for user in contributors if user.user_id is not None} + + project.organization = db_organization.name + project.organization_logo = db_organization.logo + project.total_contributors = len(unique_user_ids) + project.total_submission = await submission_crud.get_submission_count_of_a_project(db, project_id) + project.total_tasks = await tasks_crud.get_task_count_in_project(db, project_id) + + return project \ No newline at end of file diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index ccf774cf1c..11fb9d9146 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -1216,3 +1216,21 @@ async def get_template_file( return FileResponse( file_path, media_type="application/octet-stream", filename=filename ) + + +@router.get("/project_dashboard/{project_id}", response_model=project_schemas.ProjectDashboard) +async def project_dashboard( + project_id: int, db: Session = Depends(database.get_db) +): + """ + Get the project dashboard details. + + Args: + project_id (int): The ID of the project. + db (Session): The database session. + + Returns: + ProjectDashboard: The project dashboard details. + """ + + return await project_crud.get_dashboard_detail(project_id, db) \ No newline at end of file diff --git a/src/backend/app/projects/project_schemas.py b/src/backend/app/projects/project_schemas.py index 8a81d3aff5..1060272947 100644 --- a/src/backend/app/projects/project_schemas.py +++ b/src/backend/app/projects/project_schemas.py @@ -17,10 +17,11 @@ # import uuid +from datetime import datetime from typing import List, Optional from geojson_pydantic import Feature as GeojsonFeature -from pydantic import BaseModel +from pydantic import BaseModel, validator from app.db import db_models from app.models.enums import ProjectPriority, ProjectStatus, TaskSplitType @@ -143,3 +144,38 @@ class ProjectOut(ProjectBase): class BackgroundTaskStatus(BaseModel): status: str message: Optional[str] = None + + +class ProjectDashboard(BaseModel): + project_name_prefix: str + organization: str + organization_logo: Optional[str] = None + total_tasks: int + total_submission: int + total_contributors: int + created: datetime + last_active: Optional[str] = None + + @validator("created", pre=False, always=True) + def get_created(cls, value, values): + date = value.strftime("%d %b %Y") + return date + + @validator("last_active", pre=False, always=True) + def get_last_active(cls, value, values): + if value is None: + return None + + current_date = datetime.now() + time_difference = current_date - datetime.strptime(value, "%Y-%m-%d %H:%M:%S.%f") + + days_difference = time_difference.days + + if days_difference == 0: + return 'today' + elif days_difference == 1: + return 'yesterday' + elif days_difference < 7: + return f'{days_difference} day{"s" if days_difference > 1 else ""} ago' + else: + return value.strftime("%d %b %Y") \ No newline at end of file diff --git a/src/backend/app/s3.py b/src/backend/app/s3.py index c410ad2a50..44854a46df 100644 --- a/src/backend/app/s3.py +++ b/src/backend/app/s3.py @@ -108,12 +108,16 @@ def get_obj_from_bucket(bucket_name: str, s3_path: str) -> BytesIO: s3_path = f"/{s3_path}" client = s3_client() + response = None try: response = client.get_object(bucket_name, s3_path) return BytesIO(response.read()) + except Exception as e: + raise ValueError(str(e)) finally: - response.close() - response.release_conn() + if response: + response.close() + response.release_conn() def copy_obj_bucket_to_bucket(