From 886d5f506b91947e8254b79d51c6b9932e33aad0 Mon Sep 17 00:00:00 2001 From: Sujan Adhikari Date: Tue, 17 Sep 2024 17:59:15 +0545 Subject: [PATCH 1/4] feat: created an endpoint to return submission photo --- .../app/submissions/submission_routes.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/backend/app/submissions/submission_routes.py b/src/backend/app/submissions/submission_routes.py index 50b326199..96e518d4b 100644 --- a/src/backend/app/submissions/submission_routes.py +++ b/src/backend/app/submissions/submission_routes.py @@ -27,13 +27,14 @@ from fastapi.concurrency import run_in_threadpool from fastapi.responses import FileResponse, JSONResponse, Response from loguru import logger as log +from sqlalchemy import text from sqlalchemy.orm import Session from app.auth.auth_schemas import AuthUser, ProjectUserDict from app.auth.osm import login_required from app.auth.roles import mapper, project_manager from app.central import central_crud -from app.db import database, postgis_utils +from app.db import database, db_models, postgis_utils from app.models.enums import HTTPStatus, ReviewStateEnum from app.projects import project_crud, project_deps from app.submissions import submission_crud, submission_schemas @@ -594,3 +595,26 @@ async def conflate_geojson( raise HTTPException( status_code=500, detail=f"Failed to process conflation: {str(e)}" ) from e + + +@router.get("/get-submission-photo") +async def submission_photo( + submission_id: str, + db: Session = Depends(database.get_db), +): + """Get submission photo.""" + try: + sql = text(""" + select s3_path from submission_photos where submission_id = :submission_id + """) + db_result = db.execute(sql, {"submission_id": submission_id}) + result = db_result.first() + return db_models.DbSubmissionPhotos(**result._asdict()) + except Exception as e: + log.warning( + f"Failed to get submission photo for submission {submission_id}: {e}" + ) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + detail="Failed to get submission photo", + ) from e From 1bd7ffdce299c0011ec32da4300c61d74eeda3dc Mon Sep 17 00:00:00 2001 From: Sujan Adhikari Date: Wed, 18 Sep 2024 11:04:16 +0545 Subject: [PATCH 2/4] refactor: proper docstring with return type --- .../app/submissions/submission_routes.py | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/backend/app/submissions/submission_routes.py b/src/backend/app/submissions/submission_routes.py index 96e518d4b..edd8513a8 100644 --- a/src/backend/app/submissions/submission_routes.py +++ b/src/backend/app/submissions/submission_routes.py @@ -34,7 +34,7 @@ from app.auth.osm import login_required from app.auth.roles import mapper, project_manager from app.central import central_crud -from app.db import database, db_models, postgis_utils +from app.db import database, postgis_utils from app.models.enums import HTTPStatus, ReviewStateEnum from app.projects import project_crud, project_deps from app.submissions import submission_crud, submission_schemas @@ -601,15 +601,36 @@ async def conflate_geojson( async def submission_photo( submission_id: str, db: Session = Depends(database.get_db), -): - """Get submission photo.""" +) -> dict: + """Get submission photo. + + Retrieves the S3 path of the submission photo for the given submission ID. + + Args: + submission_id (str): The ID of the submission. + db (Session): The database session. + + Returns: + dict: A dictionary containing the S3 path of the submission photo. + If no photo is found, + the dictionary will contain a None value for the S3 path. + + Raises: + HTTPException: If an error occurs while retrieving the submission photo. + """ try: sql = text(""" - select s3_path from submission_photos where submission_id = :submission_id + SELECT s3_path FROM submission_photos WHERE submission_id = :submission_id """) - db_result = db.execute(sql, {"submission_id": submission_id}) - result = db_result.first() - return db_models.DbSubmissionPhotos(**result._asdict()) + result = db.execute(sql, {"submission_id": submission_id}).first() + + # Return None for s3_path if no result is found + if result is None: + log.info(f"Submission photo not found for submission ID: {submission_id}") + return {"s3_path": None} + + # Return the s3_path if the result is found + return {"s3_path": result.s3_path} except Exception as e: log.warning( f"Failed to get submission photo for submission {submission_id}: {e}" From da7de12391dce0e1bc5e4cc2223aaf9d3a959f29 Mon Sep 17 00:00:00 2001 From: Sujan Adhikari Date: Wed, 18 Sep 2024 12:46:59 +0545 Subject: [PATCH 3/4] return image url in list for the possibility of multiple images --- src/backend/app/submissions/submission_routes.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/backend/app/submissions/submission_routes.py b/src/backend/app/submissions/submission_routes.py index edd8513a8..1557d19fc 100644 --- a/src/backend/app/submissions/submission_routes.py +++ b/src/backend/app/submissions/submission_routes.py @@ -622,15 +622,12 @@ async def submission_photo( sql = text(""" SELECT s3_path FROM submission_photos WHERE submission_id = :submission_id """) - result = db.execute(sql, {"submission_id": submission_id}).first() + results = db.execute(sql, {"submission_id": submission_id}).fetchall() - # Return None for s3_path if no result is found - if result is None: - log.info(f"Submission photo not found for submission ID: {submission_id}") - return {"s3_path": None} + # Extract the s3_path from each result and return as a list + s3_paths = [result.s3_path for result in results] if results else [] - # Return the s3_path if the result is found - return {"s3_path": result.s3_path} + return {"image_url": s3_paths} except Exception as e: log.warning( f"Failed to get submission photo for submission {submission_id}: {e}" From 215211c34faea9522d02488f27d4f53438eb83ed Mon Sep 17 00:00:00 2001 From: spwoodcock Date: Sun, 22 Sep 2024 00:17:01 +0100 Subject: [PATCH 4/4] refactor: small refactor to use path params --- .../app/submissions/submission_routes.py | 93 ++++--------------- src/frontend/src/views/SubmissionDetails.tsx | 6 +- 2 files changed, 17 insertions(+), 82 deletions(-) diff --git a/src/backend/app/submissions/submission_routes.py b/src/backend/app/submissions/submission_routes.py index 1557d19fc..a40d917ef 100644 --- a/src/backend/app/submissions/submission_routes.py +++ b/src/backend/app/submissions/submission_routes.py @@ -414,74 +414,7 @@ async def submission_table( return response -# FIXME remove it since separate endpoint is not required now. -# @router.get("/task_submissions/{project_id}") -# async def task_submissions( -# task_id: int, -# project: db_models.DbProject = Depends(project_deps.get_project_by_id), -# page: int = Query(1, ge=1), -# limit: int = Query(13, le=100), -# submission_id: Optional[str] = None, -# submitted_by: Optional[str] = None, -# review_state: Optional[str] = None, -# submitted_date: Optional[str] = Query( -# None, title="Submitted Date", description="Date in format (e.g., 'YYYY-MM-DD')" -# ), -# db: Session = Depends(database.get_db), -# current_user: AuthUser = Depends(mapper), -# ): -# """This api returns the submission table of a project. - -# It takes two parameter: project_id and task_id. - -# project_id: The ID of the project. - -# task_id: The ID of the task. -# """ -# skip = (page - 1) * limit -# filters = { -# "$top": limit, -# "$skip": skip, -# "$count": True, -# "$wkt": True, -# } - -# if submitted_date: -# filters["$filter"] = ( -# "__system/submissionDate ge {}T00:00:00+00:00 " -# "and __system/submissionDate le {}T23:59:59.999+00:00" -# ).format(submitted_date, submitted_date) - -# if submitted_by: -# if "$filter" in filters: -# filters["$filter"] += f"and (username eq '{submitted_by}')" -# else: -# filters["$filter"] = f"username eq '{submitted_by}'" - -# if review_state: -# if "$filter" in filters: -# filters["$filter"] += f" and (__system/reviewState eq '{review_state}')" -# else: -# filters["$filter"] = f"__system/reviewState eq '{review_state}'" - -# data, count = await submission_crud.get_submission_by_task( -# project, task_id, filters, db -# ) -# pagination = await project_crud.get_pagination(page, count, limit, count) -# response = submission_schemas.PaginatedSubmissions( -# results=data, -# pagination=submission_schemas.PaginationInfo(**pagination.model_dump()), -# ) -# if submission_id: -# submission_detail = await submission_crud.get_submission_detail( -# project, task_id, submission_id, db -# ) -# response = submission_detail.get("value", [])[0] - -# return response - - -@router.get("/submission-detail") +@router.get("/{submission_id}") async def submission_detail( submission_id: str, db: Session = Depends(database.get_db), @@ -597,14 +530,14 @@ async def conflate_geojson( ) from e -@router.get("/get-submission-photo") +@router.get("/{submission_id}/photos") async def submission_photo( submission_id: str, db: Session = Depends(database.get_db), ) -> dict: - """Get submission photo. + """Get submission photos. - Retrieves the S3 path of the submission photo for the given submission ID. + Retrieves the S3 paths of the submission photos for the given submission ID. Args: submission_id (str): The ID of the submission. @@ -612,27 +545,33 @@ async def submission_photo( Returns: dict: A dictionary containing the S3 path of the submission photo. - If no photo is found, - the dictionary will contain a None value for the S3 path. + If no photo is found, + the dictionary will contain a None value for the S3 path. Raises: HTTPException: If an error occurs while retrieving the submission photo. """ try: sql = text(""" - SELECT s3_path FROM submission_photos WHERE submission_id = :submission_id + SELECT + s3_path + FROM + submission_photos + WHERE + submission_id = :submission_id; """) results = db.execute(sql, {"submission_id": submission_id}).fetchall() # Extract the s3_path from each result and return as a list s3_paths = [result.s3_path for result in results] if results else [] - return {"image_url": s3_paths} + return {"image_urls": s3_paths} + except Exception as e: log.warning( - f"Failed to get submission photo for submission {submission_id}: {e}" + f"Failed to get submission photos for submission {submission_id}: {e}" ) raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, - detail="Failed to get submission photo", + detail="Failed to get submission photos", ) from e diff --git a/src/frontend/src/views/SubmissionDetails.tsx b/src/frontend/src/views/SubmissionDetails.tsx index ffb677452..e103716ee 100644 --- a/src/frontend/src/views/SubmissionDetails.tsx +++ b/src/frontend/src/views/SubmissionDetails.tsx @@ -109,11 +109,7 @@ const SubmissionDetails = () => { useEffect(() => { dispatch( - SubmissionService( - `${ - import.meta.env.VITE_API_URL - }/submission/submission-detail?submission_id=${paramsInstanceId}&project_id=${projectId}`, - ), + SubmissionService(`${import.meta.env.VITE_API_URL}/submission/${paramsInstanceId}?project_id=${projectId}`), ); }, [projectId, paramsInstanceId]);