From acacd4f2069126c8bd9f32f310793d5df5b27b49 Mon Sep 17 00:00:00 2001
From: Sujit
Date: Mon, 9 Sep 2024 12:07:06 +0545
Subject: [PATCH 01/25] feat(project-dashboard): show project id insted of slug
on project cards
---
src/frontend/src/components/Projects/ProjectCard/index.tsx | 5 ++---
src/frontend/src/views/Projects/index.tsx | 1 -
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/frontend/src/components/Projects/ProjectCard/index.tsx b/src/frontend/src/components/Projects/ProjectCard/index.tsx
index 57019075..11a6e677 100644
--- a/src/frontend/src/components/Projects/ProjectCard/index.tsx
+++ b/src/frontend/src/components/Projects/ProjectCard/index.tsx
@@ -4,7 +4,6 @@ interface IProjectCardProps {
id: number;
title: string;
description: string;
- slug: string;
imageUrl: string | null;
}
@@ -12,7 +11,7 @@ export default function ProjectCard({
id,
title,
description,
- slug,
+
imageUrl,
}: IProjectCardProps) {
const navigate = useNavigate();
@@ -40,7 +39,7 @@ export default function ProjectCard({
)}
- {slug}
+ #{id}
{title}
diff --git a/src/frontend/src/views/Projects/index.tsx b/src/frontend/src/views/Projects/index.tsx
index 0431554e..8786db87 100644
--- a/src/frontend/src/views/Projects/index.tsx
+++ b/src/frontend/src/views/Projects/index.tsx
@@ -46,7 +46,6 @@ const Projects = () => {
imageUrl={project?.image_url}
title={project.name}
description={project.description}
- slug={project?.slug}
/>
),
)
From fabdbf62a6e288468273286ce3d3fafe627785d2 Mon Sep 17 00:00:00 2001
From: Sujit
Date: Mon, 9 Sep 2024 12:09:00 +0545
Subject: [PATCH 02/25] fix(project-dashboard): map crash issue on 0 project
---
.../components/Projects/MapSection/index.tsx | 57 ++++++++++---------
1 file changed, 29 insertions(+), 28 deletions(-)
diff --git a/src/frontend/src/components/Projects/MapSection/index.tsx b/src/frontend/src/components/Projects/MapSection/index.tsx
index 559d84cd..8ac709dd 100644
--- a/src/frontend/src/components/Projects/MapSection/index.tsx
+++ b/src/frontend/src/components/Projects/MapSection/index.tsx
@@ -26,37 +26,38 @@ const ProjectsMapSection = () => {
},
disableRotation: true,
});
- const { data: projectsList, isLoading } = useGetProjectsListQuery({
- select: (data: any) => {
- // find all polygons centroid and set to geojson save to single geojson
- const combinedGeojson = data?.data?.reduce(
- (acc: Record, current: Record) => {
- return {
- ...acc,
- features: [
- ...acc.features,
- {
- ...centroid(current.outline),
- properties: {
- id: current?.id,
- name: current?.name,
- slug: current?.slug,
+ const { data: projectsList, isLoading }: Record =
+ useGetProjectsListQuery({
+ select: (data: any) => {
+ // find all polygons centroid and set to geojson save to single geojson
+ const combinedGeojson = data?.data?.reduce(
+ (acc: Record, current: Record) => {
+ return {
+ ...acc,
+ features: [
+ ...acc.features,
+ {
+ ...centroid(current.outline),
+ properties: {
+ id: current?.id,
+ name: current?.name,
+ slug: current?.slug,
+ },
},
- },
- ],
- };
- },
- {
- type: 'FeatureCollection',
- features: [],
- },
- );
- return combinedGeojson;
- },
- });
+ ],
+ };
+ },
+ {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ );
+ return combinedGeojson;
+ },
+ });
useEffect(() => {
- if (!projectsList) return;
+ if (!projectsList || !projectsList?.features?.length) return;
const bbox = getBbox(projectsList as FeatureCollection);
map?.fitBounds(bbox as LngLatBoundsLike, { padding: 100 });
}, [projectsList, map]);
From 416ef00733222e65335e659258c03ce236e2e27e Mon Sep 17 00:00:00 2001
From: Pradip-p
Date: Tue, 10 Sep 2024 10:44:13 +0545
Subject: [PATCH 03/25] feat: add reset task if locked
---
src/backend/app/models/enums.py | 2 ++
src/backend/app/tasks/task_logic.py | 32 ++++++++++++++++++++++++++++
src/backend/app/tasks/task_routes.py | 24 +++++++++++++++++++++
3 files changed, 58 insertions(+)
diff --git a/src/backend/app/models/enums.py b/src/backend/app/models/enums.py
index 2a3d3ddd..6d404d3f 100644
--- a/src/backend/app/models/enums.py
+++ b/src/backend/app/models/enums.py
@@ -161,6 +161,7 @@ class EventType(str, Enum):
- ``split`` -- Set the state *unlocked done* then generate additional subdivided task areas.
- ``assign`` -- For a requester user to assign a task to another user. Set the state *locked for mapping* passing in the required user id.
- ``comment`` -- Keep the state the same, but simply add a comment.
+ - ``reset`` -- Reset a task state by unlocking it if it's locked.
Note that ``task_id`` must be specified in the endpoint too.
"""
@@ -175,3 +176,4 @@ class EventType(str, Enum):
SPLIT = "split"
ASSIGN = "assign"
COMMENT = "comment"
+ RESET = "reset"
diff --git a/src/backend/app/tasks/task_logic.py b/src/backend/app/tasks/task_logic.py
index 68ec2210..f8bdb05d 100644
--- a/src/backend/app/tasks/task_logic.py
+++ b/src/backend/app/tasks/task_logic.py
@@ -128,3 +128,35 @@ async def request_mapping(
)
result = await cur.fetchone()
return result
+
+
+async def get_task_state(
+ db: Connection, project_id: uuid.UUID, task_id: uuid.UUID
+) -> dict:
+ """
+ Retrieve the latest state of a task by querying the task_events table.
+
+ Args:
+ db (Connection): The database connection.
+ project_id (uuid.UUID): The project ID.
+ task_id (uuid.UUID): The task ID.
+
+ Returns:
+ dict: A dictionary containing the task's state and associated metadata.
+ """
+ async with db.cursor(row_factory=dict_row) as cur:
+ await cur.execute(
+ """
+ SELECT state, user_id, created_at, comment
+ FROM task_events
+ WHERE project_id = %(project_id)s AND task_id = %(task_id)s
+ ORDER BY created_at DESC
+ LIMIT 1;
+ """,
+ {
+ "project_id": str(project_id),
+ "task_id": str(task_id),
+ },
+ )
+ result = await cur.fetchone()
+ return result
diff --git a/src/backend/app/tasks/task_routes.py b/src/backend/app/tasks/task_routes.py
index c7323d46..7d67576a 100644
--- a/src/backend/app/tasks/task_routes.py
+++ b/src/backend/app/tasks/task_routes.py
@@ -368,4 +368,28 @@ async def new_event(
State.UNFLYABLE_TASK,
)
+ case EventType.RESET:
+ current_task_state = await task_logic.get_task_state(
+ db, project_id, task_id
+ )
+
+ if (
+ current_task_state["state"] == State.LOCKED_FOR_MAPPING.name
+ and user_id == current_task_state["user_id"]
+ ):
+ # Task is locked by the user, so reset it (unlock)
+ return await task_logic.update_task_state(
+ db,
+ project_id,
+ task_id,
+ user_id,
+ "Task reset by user",
+ State.LOCKED_FOR_MAPPING,
+ State.UNLOCKED_TO_MAP,
+ )
+ raise HTTPException(
+ status_code=400,
+ detail="Task is not locked by the user or cannot be reset.",
+ )
+
return True
From de97d79182d4face56fd52bdb48b50f0accc3300 Mon Sep 17 00:00:00 2001
From: Pradip-p
Date: Tue, 10 Sep 2024 10:46:30 +0545
Subject: [PATCH 04/25] feat: add exception handling in get task state
---
src/backend/app/tasks/task_logic.py | 62 ++++++++++++++++-------------
1 file changed, 35 insertions(+), 27 deletions(-)
diff --git a/src/backend/app/tasks/task_logic.py b/src/backend/app/tasks/task_logic.py
index f8bdb05d..ca962312 100644
--- a/src/backend/app/tasks/task_logic.py
+++ b/src/backend/app/tasks/task_logic.py
@@ -131,32 +131,40 @@ async def request_mapping(
async def get_task_state(
- db: Connection, project_id: uuid.UUID, task_id: uuid.UUID
-) -> dict:
- """
- Retrieve the latest state of a task by querying the task_events table.
+ db: Connection, project_id: uuid.UUID, task_id: uuid.UUID
+ ) -> dict:
+ """
+ Retrieve the latest state of a task by querying the task_events table.
+
+ Args:
+ db (Connection): The database connection.
+ project_id (uuid.UUID): The project ID.
+ task_id (uuid.UUID): The task ID.
+
+ Returns:
+ dict: A dictionary containing the task's state and associated metadata.
+ """
+ try:
+ async with db.cursor(row_factory=dict_row) as cur:
+ await cur.execute(
+ """
+ SELECT state, user_id, created_at, comment
+ FROM task_events
+ WHERE project_id = %(project_id)s AND task_id = %(task_id)s
+ ORDER BY created_at DESC
+ LIMIT 1;
+ """,
+ {
+ "project_id": str(project_id),
+ "task_id": str(task_id),
+ },
+ )
+ result = await cur.fetchone()
+ return result
+ except Exception as e:
+ raise HTTPException(
+ status_code=500,
+ detail=f"An error occurred while retrieving the task state: {str(e)}"
+ )
- Args:
- db (Connection): The database connection.
- project_id (uuid.UUID): The project ID.
- task_id (uuid.UUID): The task ID.
- Returns:
- dict: A dictionary containing the task's state and associated metadata.
- """
- async with db.cursor(row_factory=dict_row) as cur:
- await cur.execute(
- """
- SELECT state, user_id, created_at, comment
- FROM task_events
- WHERE project_id = %(project_id)s AND task_id = %(task_id)s
- ORDER BY created_at DESC
- LIMIT 1;
- """,
- {
- "project_id": str(project_id),
- "task_id": str(task_id),
- },
- )
- result = await cur.fetchone()
- return result
From a864954da414c76bec8bb73f0febec1aefc706f3 Mon Sep 17 00:00:00 2001
From: Pradip-p
Date: Tue, 10 Sep 2024 10:59:06 +0545
Subject: [PATCH 05/25] feat: add proper execption handling in reset task
---
src/backend/app/tasks/task_logic.py | 68 ++++++++++++++--------------
src/backend/app/tasks/task_routes.py | 41 ++++++++++-------
2 files changed, 58 insertions(+), 51 deletions(-)
diff --git a/src/backend/app/tasks/task_logic.py b/src/backend/app/tasks/task_logic.py
index ca962312..c44c11b0 100644
--- a/src/backend/app/tasks/task_logic.py
+++ b/src/backend/app/tasks/task_logic.py
@@ -131,40 +131,38 @@ async def request_mapping(
async def get_task_state(
- db: Connection, project_id: uuid.UUID, task_id: uuid.UUID
- ) -> dict:
- """
- Retrieve the latest state of a task by querying the task_events table.
-
- Args:
- db (Connection): The database connection.
- project_id (uuid.UUID): The project ID.
- task_id (uuid.UUID): The task ID.
-
- Returns:
- dict: A dictionary containing the task's state and associated metadata.
- """
- try:
- async with db.cursor(row_factory=dict_row) as cur:
- await cur.execute(
- """
- SELECT state, user_id, created_at, comment
- FROM task_events
- WHERE project_id = %(project_id)s AND task_id = %(task_id)s
- ORDER BY created_at DESC
- LIMIT 1;
- """,
- {
- "project_id": str(project_id),
- "task_id": str(task_id),
- },
- )
- result = await cur.fetchone()
- return result
- except Exception as e:
- raise HTTPException(
- status_code=500,
- detail=f"An error occurred while retrieving the task state: {str(e)}"
- )
+ db: Connection, project_id: uuid.UUID, task_id: uuid.UUID
+) -> dict:
+ """
+ Retrieve the latest state of a task by querying the task_events table.
+ Args:
+ db (Connection): The database connection.
+ project_id (uuid.UUID): The project ID.
+ task_id (uuid.UUID): The task ID.
+ Returns:
+ dict: A dictionary containing the task's state and associated metadata.
+ """
+ try:
+ async with db.cursor(row_factory=dict_row) as cur:
+ await cur.execute(
+ """
+ SELECT state, user_id, created_at, comment
+ FROM task_events
+ WHERE project_id = %(project_id)s AND task_id = %(task_id)s
+ ORDER BY created_at DESC
+ LIMIT 1;
+ """,
+ {
+ "project_id": str(project_id),
+ "task_id": str(task_id),
+ },
+ )
+ result = await cur.fetchone()
+ return result
+ except Exception as e:
+ raise HTTPException(
+ status_code=500,
+ detail=f"An error occurred while retrieving the task state: {str(e)}",
+ )
diff --git a/src/backend/app/tasks/task_routes.py b/src/backend/app/tasks/task_routes.py
index 7d67576a..47e3cab7 100644
--- a/src/backend/app/tasks/task_routes.py
+++ b/src/backend/app/tasks/task_routes.py
@@ -369,27 +369,36 @@ async def new_event(
)
case EventType.RESET:
+ # Fetch the task state
current_task_state = await task_logic.get_task_state(
db, project_id, task_id
)
- if (
- current_task_state["state"] == State.LOCKED_FOR_MAPPING.name
- and user_id == current_task_state["user_id"]
- ):
- # Task is locked by the user, so reset it (unlock)
- return await task_logic.update_task_state(
- db,
- project_id,
- task_id,
- user_id,
- "Task reset by user",
- State.LOCKED_FOR_MAPPING,
- State.UNLOCKED_TO_MAP,
+ # Extract state and user from the result
+ state = current_task_state.get("state")
+ locked_user_id = current_task_state.get("user_id")
+
+ # Determine error conditions in a single pass
+ if state != State.LOCKED_FOR_MAPPING.name:
+ raise HTTPException(
+ status_code=400,
+ detail="Task state does not match expected state for reset operation.",
)
- raise HTTPException(
- status_code=400,
- detail="Task is not locked by the user or cannot be reset.",
+ if user_id != locked_user_id:
+ raise HTTPException(
+ status_code=403,
+ detail="You cannot unlock this task as it is locked by another user.",
+ )
+
+ # Proceed with resetting the task
+ return await task_logic.update_task_state(
+ db,
+ project_id,
+ task_id,
+ user_id,
+ f"Task has been reset by user {user_data.name}.",
+ State.LOCKED_FOR_MAPPING,
+ State.UNLOCKED_TO_MAP,
)
return True
From fc7c826fd3dd0293032928fbae971040246c3879 Mon Sep 17 00:00:00 2001
From: Pradip-p
Date: Tue, 10 Sep 2024 11:24:39 +0545
Subject: [PATCH 06/25] fix: changes reset to unlock key
---
src/backend/app/models/enums.py | 4 ++--
src/backend/app/tasks/task_routes.py | 11 +++++------
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/src/backend/app/models/enums.py b/src/backend/app/models/enums.py
index 6d404d3f..2282f5ca 100644
--- a/src/backend/app/models/enums.py
+++ b/src/backend/app/models/enums.py
@@ -161,7 +161,7 @@ class EventType(str, Enum):
- ``split`` -- Set the state *unlocked done* then generate additional subdivided task areas.
- ``assign`` -- For a requester user to assign a task to another user. Set the state *locked for mapping* passing in the required user id.
- ``comment`` -- Keep the state the same, but simply add a comment.
- - ``reset`` -- Reset a task state by unlocking it if it's locked.
+ - ``unlock`` -- Unlock a task state by unlocking it if it's locked.
Note that ``task_id`` must be specified in the endpoint too.
"""
@@ -176,4 +176,4 @@ class EventType(str, Enum):
SPLIT = "split"
ASSIGN = "assign"
COMMENT = "comment"
- RESET = "reset"
+ UNLOCK = "unlock"
diff --git a/src/backend/app/tasks/task_routes.py b/src/backend/app/tasks/task_routes.py
index 47e3cab7..890ad7fa 100644
--- a/src/backend/app/tasks/task_routes.py
+++ b/src/backend/app/tasks/task_routes.py
@@ -368,21 +368,20 @@ async def new_event(
State.UNFLYABLE_TASK,
)
- case EventType.RESET:
+ case EventType.UNLOCK:
# Fetch the task state
current_task_state = await task_logic.get_task_state(
db, project_id, task_id
)
- # Extract state and user from the result
state = current_task_state.get("state")
locked_user_id = current_task_state.get("user_id")
- # Determine error conditions in a single pass
+ # Determine error conditions
if state != State.LOCKED_FOR_MAPPING.name:
raise HTTPException(
status_code=400,
- detail="Task state does not match expected state for reset operation.",
+ detail="Task state does not match expected state for unlock operation.",
)
if user_id != locked_user_id:
raise HTTPException(
@@ -390,13 +389,13 @@ async def new_event(
detail="You cannot unlock this task as it is locked by another user.",
)
- # Proceed with resetting the task
+ # Proceed with unlocking the task
return await task_logic.update_task_state(
db,
project_id,
task_id,
user_id,
- f"Task has been reset by user {user_data.name}.",
+ f"Task has been unlock by user {user_data.name}.",
State.LOCKED_FOR_MAPPING,
State.UNLOCKED_TO_MAP,
)
From d2bd55ae914e96b4540f58a124913fba9ba6a035 Mon Sep 17 00:00:00 2001
From: Sujit
Date: Tue, 10 Sep 2024 13:19:25 +0545
Subject: [PATCH 07/25] feat(project-creation): add task dimension limitation
to 50-700
---
.../FormContents/GenerateTask/index.tsx | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx b/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx
index 6f1a015c..36cf3f57 100644
--- a/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx
+++ b/src/frontend/src/components/CreateProject/FormContents/GenerateTask/index.tsx
@@ -1,4 +1,6 @@
import { useMutation } from '@tanstack/react-query';
+import { useState } from 'react';
+import ErrorMessage from '@Components/common/ErrorMessage';
import { useTypedDispatch, useTypedSelector } from '@Store/hooks';
import { FormControl, Label, Input } from '@Components/common/FormUI';
import { Button } from '@Components/RadixComponents/Button';
@@ -11,6 +13,7 @@ import MapSection from './MapSection';
export default function GenerateTask({ formProps }: { formProps: any }) {
const dispatch = useTypedDispatch();
+ const [error, setError] = useState('');
const { register, watch } = formProps;
const dimension = watch('task_split_dimension');
@@ -53,11 +56,15 @@ export default function GenerateTask({ formProps }: { formProps: any }) {
type="number"
className="naxatw-mt-1"
value={dimension}
+ min={50}
+ max={700}
{...register('task_split_dimension', {
required: 'Required',
valueAsNumber: true,
})}
+ onFocus={() => setError('')}
/>
+ {error && }