Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/download orthophoto #267

Merged
merged 5 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/backend/app/projects/project_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""
Expand Down
2 changes: 1 addition & 1 deletion src/backend/app/projects/project_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
44 changes: 36 additions & 8 deletions src/backend/app/projects/project_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -457,10 +492,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]
4 changes: 0 additions & 4 deletions src/backend/app/tasks/task_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = [
{
Expand All @@ -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 (
<div
className="naxatw-group naxatw-flex naxatw-cursor-pointer naxatw-items-center naxatw-gap-1 naxatw-text-center naxatw-font-semibold naxatw-text-red"
tabIndex={0}
role="button"
onKeyDown={() => {}}
onClick={() => handleDownloadResult()}
>
<div className="group-hover:naxatw-underline">Download</div>
<Icon className="!naxatw-text-icon-sm" name="download" />
</div>
);
},
},
];

export default function TableSection() {
Expand All @@ -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,
},
];
}, []);
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/views/IndividualProject/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const IndividualProject = () => {
{/* <----------- temporary breadcrumb -----------> */}
</div>
<div className="naxatw-flex naxatw-flex-col naxatw-gap-6 md:naxatw-flex-row">
<div className="naxatw-order-2 naxatw-w-full naxatw-max-w-[27rem]">
<div className="naxatw-order-2 naxatw-w-full naxatw-max-w-[30rem]">
<Tab
orientation="row"
className="naxatw-bg-transparent hover:naxatw-border-b-2 hover:naxatw-border-red"
Expand Down