Skip to content

Commit

Permalink
feat(backend): project dashboard endpoint (#1054)
Browse files Browse the repository at this point in the history
* Created Project dashboard

* added organization info

* Exception handling in s3 if no object found

* refactor

---------

Co-authored-by: sujanadh <sujanadh07@gmail.com>
  • Loading branch information
Sujanadh and sujanadh authored Dec 23, 2023
1 parent 6b2847c commit db32567
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 3 deletions.
27 changes: 27 additions & 0 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/"
Expand Down Expand Up @@ -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
18 changes: 18 additions & 0 deletions src/backend/app/projects/project_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
38 changes: 37 additions & 1 deletion src/backend/app/projects/project_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
8 changes: 6 additions & 2 deletions src/backend/app/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit db32567

Please sign in to comment.