-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #62 from hotosm/task_events
Update task states for each task.
- Loading branch information
Showing
8 changed files
with
379 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
""" | ||
Revision ID: 06668eb5d14a | ||
Revises: fa5c74996273 | ||
Create Date: 2024-07-09 04:17:49.816148 | ||
""" | ||
from typing import Sequence, Union | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision: str = "06668eb5d14a" | ||
down_revision: Union[str, None] = "fa5c74996273" | ||
branch_labels: Union[str, Sequence[str], None] = None | ||
depends_on: Union[str, Sequence[str], None] = None | ||
|
||
|
||
# Define the new enum type | ||
new_state_enum = sa.Enum( | ||
"UNLOCKED_TO_MAP", | ||
"LOCKED_FOR_MAPPING", | ||
"UNLOCKED_TO_VALIDATE", | ||
"LOCKED_FOR_VALIDATION", | ||
"UNLOCKED_DONE", | ||
"REQUEST_FOR_MAPPING", | ||
name="state", | ||
) | ||
|
||
old_state_enum = sa.Enum( | ||
"UNLOCKED_TO_MAP", | ||
"LOCKED_FOR_MAPPING", | ||
"UNLOCKED_TO_VALIDATE", | ||
"LOCKED_FOR_VALIDATION", | ||
"UNLOCKED_DONE", | ||
name="state", | ||
) | ||
|
||
|
||
def upgrade(): | ||
op.execute("ALTER TYPE state ADD VALUE 'REQUEST_FOR_MAPPING'") | ||
|
||
|
||
def downgrade(): | ||
# Downgrade the enum type by recreating it without the new value | ||
op.execute("ALTER TYPE state RENAME TO state_old") | ||
old_state_enum.create(op.get_bind(), checkfirst=False) | ||
op.execute( | ||
( | ||
"ALTER TABLE task_events " | ||
"ALTER COLUMN state TYPE state USING state::text::state" | ||
) | ||
) | ||
op.execute("DROP TYPE state_old") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
""" | ||
Revision ID: fa5c74996273 | ||
Revises: ac09917990dc | ||
Create Date: 2024-07-05 11:51:02.146671 | ||
""" | ||
from typing import Sequence, Union | ||
|
||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
# revision identifiers, used by Alembic. | ||
revision: str = "fa5c74996273" | ||
down_revision: Union[str, None] = "ac09917990dc" | ||
branch_labels: Union[str, Sequence[str], None] = None | ||
depends_on: Union[str, Sequence[str], None] = None | ||
|
||
# Define the existing enum type | ||
existing_taskstatus_enum = sa.Enum( | ||
"READY", | ||
"LOCKED_FOR_MAPPING", | ||
"MAPPED", | ||
"LOCKED_FOR_VALIDATION", | ||
"VALIDATED", | ||
"INVALIDATED", | ||
"BAD", | ||
"SPLIT", | ||
name="taskstatus", | ||
) | ||
|
||
# Define the new enum type | ||
new_state_enum = sa.Enum( | ||
"UNLOCKED_TO_MAP", | ||
"LOCKED_FOR_MAPPING", | ||
"UNLOCKED_TO_VALIDATE", | ||
"LOCKED_FOR_VALIDATION", | ||
"UNLOCKED_DONE", | ||
name="state", | ||
) | ||
|
||
|
||
def upgrade() -> None: | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
|
||
# Create the new enum type in the database | ||
new_state_enum.create(op.get_bind()) | ||
|
||
# Use the USING clause to convert existing column values to the new enum type | ||
op.execute( | ||
"ALTER TABLE task_events ALTER COLUMN state TYPE state USING state::text::state" | ||
) | ||
# ### end Alembic commands ### | ||
|
||
|
||
def downgrade() -> None: | ||
# ### commands auto generated by Alembic - please adjust! ### | ||
|
||
# Use the USING clause to convert back to the original enum type | ||
op.execute( | ||
"ALTER TABLE task_events ALTER COLUMN state TYPE taskstatus USING state::text::taskstatus" | ||
) | ||
|
||
# Drop the new enum type from the database | ||
new_state_enum.drop(op.get_bind()) | ||
# ### end Alembic commands ### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import uuid | ||
from databases import Database | ||
from app.models.enums import State | ||
|
||
|
||
async def all_tasks_states(db: Database, project_id: uuid.UUID): | ||
query = """ | ||
SELECT DISTINCT ON (task_id) project_id, task_id, state | ||
FROM task_events | ||
WHERE project_id=:project_id | ||
ORDER BY task_id, created_at DESC | ||
""" | ||
r = await db.fetch_all(query, {"project_id": str(project_id)}) | ||
|
||
return [dict(r) for r in r] | ||
|
||
|
||
async def request_mapping( | ||
db: Database, project_id: uuid.UUID, task_id: uuid.UUID, user_id: str, comment: str | ||
): | ||
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 | ||
), | ||
released AS ( | ||
SELECT COUNT(*) = 0 AS no_record | ||
FROM task_events | ||
WHERE project_id= :project_id AND task_id= :task_id AND state = :unlocked_to_map_state | ||
) | ||
INSERT INTO task_events (event_id, project_id, task_id, user_id, comment, state, created_at) | ||
SELECT | ||
gen_random_uuid(), | ||
:project_id, | ||
:task_id, | ||
:user_id, | ||
:comment, | ||
:request_for_map_state, | ||
now() | ||
FROM last | ||
RIGHT JOIN released ON true | ||
WHERE (last.state = :unlocked_to_map_state OR released.no_record = true); | ||
""" | ||
|
||
values = { | ||
"project_id": str(project_id), | ||
"task_id": str(task_id), | ||
"user_id": str(user_id), | ||
"comment": comment, | ||
"unlocked_to_map_state": State.UNLOCKED_TO_MAP.name, | ||
"request_for_map_state": State.REQUEST_FOR_MAPPING.name, | ||
} | ||
|
||
await db.fetch_one(query, values) | ||
|
||
return {"project_id": project_id, "task_id": task_id, "comment": comment} | ||
|
||
|
||
async def update_task_state( | ||
db: Database, | ||
project_id: uuid.UUID, | ||
task_id: uuid.UUID, | ||
user_id: str, | ||
comment: str, | ||
initial_state: 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 | ||
) | ||
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), | ||
"task_id": str(task_id), | ||
"user_id": str(user_id), | ||
"comment": comment, | ||
"initial_state": initial_state.name, | ||
"final_state": final_state.name, | ||
} | ||
|
||
await db.fetch_one(query, values) | ||
|
||
return {"project_id": project_id, "task_id": task_id, "comment": comment} | ||
|
||
|
||
async def get_requested_user_id( | ||
db: Database, project_id: uuid.UUID, task_id: uuid.UUID | ||
): | ||
query = """ | ||
SELECT user_id | ||
FROM task_events | ||
WHERE project_id = :project_id AND task_id = :task_id and state = :request_for_map_state | ||
ORDER BY created_at DESC | ||
LIMIT 1 | ||
""" | ||
values = { | ||
"project_id": str(project_id), | ||
"task_id": str(task_id), | ||
"request_for_map_state": State.REQUEST_FOR_MAPPING.name, | ||
} | ||
|
||
result = await db.fetch_one(query, values) | ||
if result is None: | ||
raise ValueError("No user requested for mapping") | ||
return result["user_id"] |
Oops, something went wrong.