Skip to content

Commit

Permalink
Merge pull request #157 from hotosm/feat/update-task-state
Browse files Browse the repository at this point in the history
Fix:  updates the handling of task events and statistics in the API.
  • Loading branch information
nrjadkry authored Aug 20, 2024
2 parents 1e25fc2 + cee7adc commit 168e14e
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 88 deletions.
42 changes: 23 additions & 19 deletions src/backend/app/projects/project_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,35 +150,39 @@ async def get_project_info_by_id(db: Database, project_id: uuid.UUID):
if not project_record:
return None
query = """
WITH TaskStateCalculation AS (
SELECT DISTINCT ON (te.task_id)
te.task_id,
te.user_id,
CASE
WHEN te.state = 'REQUEST_FOR_MAPPING' THEN 'request logs'
WHEN te.state = 'LOCKED_FOR_MAPPING' THEN 'ongoing'
WHEN te.state = 'UNLOCKED_DONE' THEN 'completed'
WHEN te.state = 'UNFLYABLE_TASK' THEN 'unflyable task'
ELSE 'UNLOCKED_TO_MAP'
END AS calculated_state
FROM
task_events te
ORDER BY
te.task_id, te.created_at DESC
)
SELECT
t.id,
t.project_task_index,
t.outline,
ST_Area(ST_Transform(t.outline, 4326)) / 1000000 AS task_area,
te.user_id,
te.state,
tsc.user_id,
u.name,
CASE
WHEN te.state = 'REQUEST_FOR_MAPPING' THEN 'request logs'
WHEN te.state = 'LOCKED_FOR_MAPPING' THEN 'ongoing'
WHEN te.state = 'UNLOCKED_DONE' THEN 'completed'
WHEN te.state = 'UNFLYABLE_TASK' THEN 'unflyable task'
ELSE '' -- Default case if the state does not match any expected values
END AS state
ST_Area(ST_Transform(t.outline, 3857)) / 1000000 AS task_area,
COALESCE(tsc.calculated_state, 'UNLOCKED_TO_MAP') AS state
FROM
tasks t
LEFT JOIN
task_events te
ON
t.id = te.task_id
TaskStateCalculation tsc ON t.id = tsc.task_id
LEFT JOIN
users u
ON
te.user_id = u.id
users u ON tsc.user_id = u.id
WHERE
t.project_id = :project_id;"""
t.project_id = :project_id;
"""

task_records = await db.fetch_all(query, {"project_id": project_id})
project_record.tasks = task_records if task_records is not None else []
Expand Down
101 changes: 50 additions & 51 deletions src/backend/app/tasks/task_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,43 +34,43 @@ async def get_task_geojson(db: Database, task_id: uuid.UUID):
return json.loads(data["geom"])


async def get_tasks_by_user(user_id: str, db: Database, role: str):
async def get_tasks_by_user(
user_id: str, db: Database, role: str, skip: int = 0, limit: int = 50
):
try:
query = """WITH task_details AS (
SELECT
tasks.id AS task_id,
task_events.project_id AS project_id,
ST_Area(ST_Transform(tasks.outline, 4326)) / 1000000 AS task_area,
task_events.created_at,
task_events.state
FROM
task_events
LEFT JOIN
tasks ON task_events.task_id = tasks.id
WHERE
(
:role = 'DRONE_PILOT' AND task_events.user_id = :user_id
)
OR
(
:role != 'DRONE_PILOT' AND task_events.project_id IN (SELECT id FROM projects WHERE author_id = :user_id)
)
)
SELECT
task_details.task_id,
task_details.project_id,
task_details.task_area,
task_details.created_at,
query = """SELECT DISTINCT ON (tasks.id)
tasks.id AS task_id,
task_events.project_id AS project_id,
ST_Area(ST_Transform(tasks.outline, 3857)) / 1000000 AS task_area,
task_events.created_at,
CASE
WHEN task_details.state = 'REQUEST_FOR_MAPPING' THEN 'request logs'
WHEN task_details.state = 'LOCKED_FOR_MAPPING' THEN 'ongoing'
WHEN task_details.state = 'UNLOCKED_DONE' THEN 'completed'
WHEN task_details.state = 'UNFLYABLE_TASK' THEN 'unflyable task'
ELSE ''
WHEN task_events.state = 'REQUEST_FOR_MAPPING' THEN 'request logs'
WHEN task_events.state = 'LOCKED_FOR_MAPPING' THEN 'ongoing'
WHEN task_events.state = 'UNLOCKED_DONE' THEN 'completed'
WHEN task_events.state = 'UNFLYABLE_TASK' THEN 'unflyable task'
ELSE 'UNLOCKED_TO_MAP'
END AS state
FROM task_details;
FROM
task_events
LEFT JOIN
tasks ON task_events.task_id = tasks.id
WHERE
(
:role = 'DRONE_PILOT' AND task_events.user_id = :user_id
)
OR
(
:role != 'DRONE_PILOT' AND task_events.project_id IN (SELECT id FROM projects WHERE author_id = :user_id)
)
ORDER BY
tasks.id, task_events.created_at DESC
OFFSET :skip
LIMIT :limit;
"""
records = await db.fetch_all(query, values={"user_id": user_id, "role": role})
records = await db.fetch_all(
query,
values={"user_id": user_id, "role": role, "skip": skip, "limit": limit},
)
return records

except Exception as e:
Expand Down Expand Up @@ -193,24 +193,23 @@ async def update_task_state(
final_state: State,
):
query = """
WITH last AS (
SELECT *
FROM task_events
WHERE project_id = :project_id AND task_id = :task_id
ORDER BY created_at DESC
LIMIT 1
),
locked AS (
SELECT *
FROM last
WHERE user_id = :user_id AND state = :initial_state
)
UPDATE task_events
SET state = :final_state,
comment = :comment,
created_at = now()
FROM locked
WHERE task_events.event_id = locked.event_id
WITH last AS (
SELECT *
FROM task_events
WHERE project_id = :project_id AND task_id = :task_id
ORDER BY created_at DESC
LIMIT 1
),
locked AS (
SELECT *
FROM last
WHERE user_id = :user_id AND state = :initial_state
)
INSERT INTO task_events(event_id, project_id, task_id, user_id, state, comment, created_at)
SELECT gen_random_uuid(), project_id, task_id, user_id, :final_state, :comment, now()
FROM last
WHERE user_id = :user_id
RETURNING project_id, task_id, user_id, state;
"""
values = {
"project_id": str(project_id),
Expand Down
44 changes: 26 additions & 18 deletions src/backend/app/tasks/task_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async def read_task(
try:
query = """
SELECT
ST_Area(ST_Transform(tasks.outline, 4326)) / 1000000 AS task_area,
ST_Area(ST_Transform(tasks.outline, 3857)) / 1000000 AS task_area,
task_events.created_at,
projects.name AS project_name,
project_task_index,
Expand Down Expand Up @@ -76,21 +76,27 @@ async def get_task_stats(

raw_sql = """
SELECT
COUNT(CASE WHEN te.state = 'REQUEST_FOR_MAPPING' THEN 1 END) AS request_logs,
COUNT(CASE WHEN te.state = 'LOCKED_FOR_MAPPING' THEN 1 END) AS ongoing_tasks,
COUNT(CASE WHEN te.state = 'UNLOCKED_DONE' THEN 1 END) AS completed_tasks,
COUNT(CASE WHEN te.state = 'UNFLYABLE_TASK' THEN 1 END) AS unflyable_tasks
FROM tasks t
LEFT JOIN task_events te ON t.id = te.task_id
WHERE
(
:role = 'DRONE_PILOT' AND te.user_id = :user_id
)
OR
(
:role != 'DRONE_PILOT' AND t.project_id IN (SELECT id FROM projects WHERE author_id = :user_id)
)
"""
COUNT(CASE WHEN te.state = 'REQUEST_FOR_MAPPING' THEN 1 END) AS request_logs,
COUNT(CASE WHEN te.state = 'LOCKED_FOR_MAPPING' THEN 1 END) AS ongoing_tasks,
COUNT(CASE WHEN te.state = 'UNLOCKED_DONE' THEN 1 END) AS completed_tasks,
COUNT(CASE WHEN te.state = 'UNFLYABLE_TASK' THEN 1 END) AS unflyable_tasks
FROM (
SELECT DISTINCT ON (te.task_id)
te.task_id,
te.state,
te.created_at
FROM task_events te
WHERE
(:role = 'DRONE_PILOT' AND te.user_id = :user_id)
OR
(:role != 'DRONE_PILOT' AND te.task_id IN (
SELECT t.id
FROM tasks t
WHERE t.project_id IN (SELECT id FROM projects WHERE author_id = :user_id)
))
ORDER BY te.task_id, te.created_at DESC
) AS te;
"""

try:
db_counts = await db.fetch_one(
Expand All @@ -108,6 +114,8 @@ async def get_task_stats(
async def list_tasks(
db: Database = Depends(database.get_db),
user_data: AuthUser = Depends(login_required),
skip: int = 0,
limit: int = 50,
):
"""Get all tasks for a all user."""

Expand All @@ -117,14 +125,14 @@ async def list_tasks(

roles = [record["role"] for record in records]
if UserRole.PROJECT_CREATOR.name in roles:
role = UserRole.PROJECT_CREATOR.name
role = "PROJECT_CREATOR"
else:
role = "DRONE_PILOT"

if not records:
raise HTTPException(status_code=404, detail="User profile not found")

return await task_crud.get_tasks_by_user(user_id, db, role)
return await task_crud.get_tasks_by_user(user_id, db, role, skip, limit)


@router.get("/states/{project_id}")
Expand Down

0 comments on commit 168e14e

Please sign in to comment.