From 5dffbed0d95115701587c357a74fa3c5538d541e Mon Sep 17 00:00:00 2001 From: Pradip-p Date: Fri, 4 Oct 2024 17:01:34 +0545 Subject: [PATCH 1/5] feat: updated downlaod assets url --- src/backend/app/projects/project_logic.py | 9 ++++- src/backend/app/projects/project_routes.py | 2 +- src/backend/app/projects/project_schemas.py | 45 +++++++++++++++++---- src/backend/app/tasks/task_schemas.py | 4 -- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/backend/app/projects/project_logic.py b/src/backend/app/projects/project_logic.py index 01a36390..e7b3f289 100644 --- a/src/backend/app/projects/project_logic.py +++ b/src/backend/app/projects/project_logic.py @@ -180,7 +180,7 @@ def process_drone_images(project_id: uuid.UUID, task_id: uuid.UUID): ) -async def get_project_info_from_s3(project_id: uuid.UUID, task_id: uuid.UUID): +def get_project_info_from_s3(project_id: uuid.UUID, task_id: uuid.UUID): """ Helper function to get the number of images and the URL to download the assets. """ @@ -219,6 +219,13 @@ async def get_project_info_from_s3(project_id: uuid.UUID, task_id: uuid.UUID): log.error(f"An error occurred while accessing assets file: {e}") raise HTTPException(status_code=500, detail=str(e)) + # return { + # "project_id":str(project_id), + # "task_id":str(task_id), + # "image_count":image_count, + # "assets_url":presigned_url, + # } + return project_schemas.AssetsInfo( project_id=str(project_id), task_id=str(task_id), diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index f6f4e0cc..5f985051 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -365,4 +365,4 @@ async def get_assets_info( """ Endpoint to get the number of images and the URL to download the assets for a given project and task. """ - return await project_logic.get_project_info_from_s3(project.id, task_id) + return project_logic.get_project_info_from_s3(project.id, task_id) diff --git a/src/backend/app/projects/project_schemas.py b/src/backend/app/projects/project_schemas.py index b06b4dc6..90a1939f 100644 --- a/src/backend/app/projects/project_schemas.py +++ b/src/backend/app/projects/project_schemas.py @@ -2,6 +2,7 @@ import uuid from typing import Annotated, Optional, List from datetime import datetime, date +from app.projects import project_logic import geojson from loguru import logger as log from pydantic import BaseModel, computed_field, Field, model_validator @@ -26,6 +27,13 @@ from app.s3 import get_image_dir_url +class AssetsInfo(BaseModel): + project_id: str + task_id: str + image_count: int + assets_url: Optional[str] + + def validate_geojson( value: FeatureCollection | Feature | Polygon, ) -> geojson.FeatureCollection: @@ -121,16 +129,41 @@ def validate_to_json(cls, value): return value +class AssetsInfoData(BaseModel): + project_id: int + + class TaskOut(BaseModel): """Base project model.""" id: uuid.UUID + project_id: uuid.UUID project_task_index: int - outline: Optional[Polygon | Feature | FeatureCollection] + outline: Optional[Polygon | Feature | FeatureCollection] = None state: Optional[str] = None user_id: Optional[str] = None task_area: Optional[float] = None name: Optional[str] = None + image_count: Optional[int] = None + assets_url: Optional[str] = None + + @model_validator(mode="after") + def set_assets_url(cls, values): + """Set image_url and image count before rendering the model.""" + task_id = values.id + project_id = values.project_id + + if task_id and project_id: + data = project_logic.get_project_info_from_s3(project_id, task_id) + if data: + return values.copy( + update={ + "assets_url": data.assets_url, + "image_count": data.image_count, + } + ) + + return values class DbProject(BaseModel): @@ -227,6 +260,7 @@ async def one(db: Connection, project_id: uuid.UUID): SELECT t.id, t.project_task_index, + t.project_id, ST_AsGeoJSON(t.outline)::jsonb -> 'coordinates' AS coordinates, ST_AsGeoJSON(t.outline)::jsonb -> 'type' AS type, ST_XMin(ST_Envelope(t.outline)) AS xmin, @@ -253,6 +287,7 @@ async def one(db: Connection, project_id: uuid.UUID): user_id, name, task_area, + project_id, jsonb_build_object( 'type', 'Feature', 'geometry', jsonb_build_object( @@ -272,6 +307,7 @@ async def one(db: Connection, project_id: uuid.UUID): ) task_records = await cur.fetchall() + print(task_records) project_record.tasks = task_records if task_records is not None else [] project_record.task_count = len(task_records) return project_record @@ -457,10 +493,3 @@ class PresignedUrlRequest(BaseModel): task_id: uuid.UUID image_name: List[str] expiry: int # Expiry time in hours - - -class AssetsInfo(BaseModel): - project_id: str - task_id: str - image_count: int - assets_url: Optional[str] diff --git a/src/backend/app/tasks/task_schemas.py b/src/backend/app/tasks/task_schemas.py index cc695e5f..cee3fa23 100644 --- a/src/backend/app/tasks/task_schemas.py +++ b/src/backend/app/tasks/task_schemas.py @@ -121,15 +121,12 @@ async def all(db: Connection, project_id: uuid.UUID): existing_tasks = await cur.fetchall() # Get all task_ids from the tasks table task_ids = await Task.get_all_tasks(db, project_id) - print("task ids = ", task_ids) # Create a set of existing task_ids for quick lookup existing_task_ids = {task.task_id for task in existing_tasks} - print("existing task ids = ", existing_task_ids) # task ids that are not in task_events table remaining_task_ids = [x for x in task_ids if x not in existing_task_ids] - print("remaining task ids = ", remaining_task_ids) # Add missing tasks with state as "UNLOCKED_FOR_MAPPING" remaining_tasks = [ @@ -140,7 +137,6 @@ async def all(db: Connection, project_id: uuid.UUID): } for task_id in remaining_task_ids ] - print("remaining tasks = ", remaining_tasks) # Combine both existing tasks and remaining tasks combined_tasks = existing_tasks + remaining_tasks return combined_tasks From 1e60042367ea60116d69d487f12f685644d84e41 Mon Sep 17 00:00:00 2001 From: Pradip-p Date: Fri, 4 Oct 2024 17:03:27 +0545 Subject: [PATCH 2/5] refac: remove dict return from get project info from s3 --- src/backend/app/projects/project_logic.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/backend/app/projects/project_logic.py b/src/backend/app/projects/project_logic.py index e7b3f289..624337b3 100644 --- a/src/backend/app/projects/project_logic.py +++ b/src/backend/app/projects/project_logic.py @@ -219,13 +219,6 @@ def get_project_info_from_s3(project_id: uuid.UUID, task_id: uuid.UUID): log.error(f"An error occurred while accessing assets file: {e}") raise HTTPException(status_code=500, detail=str(e)) - # return { - # "project_id":str(project_id), - # "task_id":str(task_id), - # "image_count":image_count, - # "assets_url":presigned_url, - # } - return project_schemas.AssetsInfo( project_id=str(project_id), task_id=str(task_id), From db9cef03f19d091d237f75dc9d4e405205ba7e1a Mon Sep 17 00:00:00 2001 From: Pradip-p Date: Fri, 4 Oct 2024 17:06:55 +0545 Subject: [PATCH 3/5] refac: remove print from get projects --- src/backend/app/projects/project_schemas.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/app/projects/project_schemas.py b/src/backend/app/projects/project_schemas.py index 90a1939f..7b4f4ab2 100644 --- a/src/backend/app/projects/project_schemas.py +++ b/src/backend/app/projects/project_schemas.py @@ -307,7 +307,6 @@ async def one(db: Connection, project_id: uuid.UUID): ) task_records = await cur.fetchall() - print(task_records) project_record.tasks = task_records if task_records is not None else [] project_record.task_count = len(task_records) return project_record From ce96f109d6ea4139bdc8c155f30062fa9fa24ec8 Mon Sep 17 00:00:00 2001 From: Sujit Date: Fri, 4 Oct 2024 17:39:40 +0545 Subject: [PATCH 4/5] fix(individual-project): table UI --- src/frontend/src/views/IndividualProject/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/views/IndividualProject/index.tsx b/src/frontend/src/views/IndividualProject/index.tsx index 58580ccb..e218493a 100644 --- a/src/frontend/src/views/IndividualProject/index.tsx +++ b/src/frontend/src/views/IndividualProject/index.tsx @@ -86,7 +86,7 @@ const IndividualProject = () => { {/* <----------- temporary breadcrumb -----------> */}
-
+
Date: Fri, 4 Oct 2024 17:40:19 +0545 Subject: [PATCH 5/5] fix(individual-project): download orhophoto --- .../Contributions/TableSection/index.tsx | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/frontend/src/components/IndividualProject/Contributions/TableSection/index.tsx b/src/frontend/src/components/IndividualProject/Contributions/TableSection/index.tsx index 7279bbeb..96b5a7c9 100644 --- a/src/frontend/src/components/IndividualProject/Contributions/TableSection/index.tsx +++ b/src/frontend/src/components/IndividualProject/Contributions/TableSection/index.tsx @@ -1,6 +1,8 @@ import DataTable from '@Components/common/DataTable'; +import Icon from '@Components/common/Icon'; import { useTypedSelector } from '@Store/hooks'; import { useMemo } from 'react'; +import { toast } from 'react-toastify'; const contributionsDataColumns = [ { @@ -15,6 +17,41 @@ const contributionsDataColumns = [ header: 'Task Status', accessorKey: 'task_state', }, + { header: 'Image count', accessorKey: 'image_count' }, + + { + header: 'Orthophoto', + accessorKey: 'assets_url', + cell: ({ row }: any) => { + const handleDownloadResult = () => { + const { original: rowData } = row; + if (!rowData?.assets_url) return; + try { + const link = document.createElement('a'); + link.href = rowData?.assets_url; + link.download = 'assets.zip'; + document.body.appendChild(link); + link.click(); + link.remove(); + } catch (error) { + toast.error(`There wan an error while downloading file ${error}`); + } + }; + + return ( +
{}} + onClick={() => handleDownloadResult()} + > +
Download
+ +
+ ); + }, + }, ]; export default function TableSection() { @@ -30,6 +67,8 @@ export default function TableSection() { user: curr?.name || '-', task_mapped: `Task# ${curr?.project_task_index}`, task_state: curr?.state, + assets_url: curr?.assets_url, + image_count: curr?.image_count, }, ]; }, []);