feat: task assignment UI for project managers (#3095) - #3097
kartikey54 wants to merge 3 commits into
Conversation
Stamp a persistent task_id into task_areas_geojson features on save, and make the ODK entity builder honor stored ids instead of re-deriving them positionally, so QField and ODK exports agree on task identity across re-finalization. Signed-off-by: kartikey54 <kartikey54@users.noreply.github.com>
Add an assignment panel to the project details page: a Leaflet task selection map with quadrant auto-grouping, per-task assignee and group labels staged in sessionStorage, saved as feature properties on task_areas_geojson. Assignment fields only; task status stays owned by the field apps. Part of hotosm#3095. Signed-off-by: kartikey54 <kartikey54@users.noreply.github.com>
Contributor Signature RequiredThank you for your contribution! Before we can accept your pull request, you need to sign our Contribution Policy. Why do I need to do this?
How to signTo sign the agreement, please comment on this PR with: I have read the CONTRIBUTING.md document and I hereby sign and agree with the guidelines You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
for more information, see https://pre-commit.ci
|
I have read the CONTRIBUTING.md document and I hereby sign and agree with the guidelines |
|
Cool! Thanks @kartikey54! Sorry for the slow review, I have been on holiday. I will check this ASAP and give feedback =) |
|
Read through the task_id stability fix, On open question #2: confirmed by grep, |
What
A first, "assign in advance" task assignment UI for project managers, addressing #3095. Managers get a panel on the project details page that shows task areas on a map, lets them select tasks and apply an assignee + group label (with one-click quadrant auto-grouping for the NE/SW/SE/NW bulk case), and persists assignments as feature properties on the existing
task_areas_geojson.This is opened as a draft for discussion — it implements the durable "edit in the browser, confirm to save" half of the issue. The field-device sync (QFieldCloud geopackage / ODK entity push) and the CoMaps
.kmzexport (#3064) are deliberately deferred to follow-up PRs (see Deferred below). I'd value a steer on the open questions before going further.How it fits the existing architecture
No SPA, no build step, no new dependencies, no schema change — entirely server-rendered LiteStar + HTMX + Jinja2 with a flat static JS file, per
AGENTS.mdand the existing decisions.Two commits:
fix(backend): stabilize task_id across save and export paths— a small prerequisite refactor. Task ids were assigned positionally and divergently in the QField (_build_tasks_geojson) and ODK (_build_task_entities) export paths, so the two ecosystems could disagree on task identity. This stamps a persistent, collision-safe integertask_idintotask_areas_geojsonfeatures on save and makes both export builders read the stored id. This is the keystone the assignment feature keys on, and is independently useful — happy to split it into its own PR if you'd prefer.feat(htmx): task assignment map panel for project managers— the panel itself:app/htmx/assignment/module with threeproject_manager-gated routes: a lazy-loaded panel fragment, a JSONassignments/geojsonendpoint (tasks merged withassigned_to/assigned_group), and aPOSTsave.save_task_assignmentsservice alongsidesave_task_areas: validates the group label (^[a-zA-Z0-9_-]*$), caps/sanitises the assignee, rejects unknown task ids, merges into feature properties under aFOR UPDATErow lock, and never writesstatus— task state stays owned by the field apps.task_assignment.js(flat file, reuses the global Leaflet): click / shift-click selection shared between map and table, quadrant auto-grouping, draft edits staged insessionStorage(matching the existingftm-draft-aoi-outlineprecedent) and replayed over fresh server data, all user values rendered viatextContentonly.Screenshots
Assignment panel — task areas on a map, quadrant-coloured by group, with the summary table below:
Selecting tasks — click / shift-click on the map and table, then apply an assignee + group to the selection:
After saving — assignments persisted to feature properties and reflected in the table:
Tests
34 new tests (
tests/htmx/test_assignment_routes.py, plus task-id stability coverage intest_finalize_odk.py/test_qfield_routes.py): route registration and auth, geojson shape and defaults, the save happy path (incl.HX-Triggerpayload and thatstatusis never written), XSS escaping, validation rejects (bad group label, overlong assignee, unknown task id, missing payload), the empty/{}no-split sentinel, and cross-builder task-id agreement. Local backend suite: 305 passed, 0 failed (the two ODK-Central / QGIS-container suites were not run locally).Deliberately deferred (happy to sequence as follow-ups)
assigned_to/assigned_groupto thetasksdataset entities (most machinery already exists incentral_crud).tasks.gpkg, merge assignment fields bytask_id, push back; plus stopping_add_plugin_task_fieldsfrom clobbering assignments on regeneration..kmzexport (Mapping app: add CoMaps #3064).Open questions
task_areas_geojsonfeature properties (this PR) vs a relationaltask_assignmentstable (audit trail, row-level locking). I went properties-first to keep this PR schema-free; would you want the table from the start?user_roles, soproject_manageronly resolves for global admins in deployments today. Should project creators get a project-admin role at creation?assigned_to(covers account-less mappers / shared QFC accounts) with opportunistic QFC-collaborator suggestions — acceptable, or should assignment drive per-mapper QFC user provisioning?assigned_grouplabel the right model for the quadrant use case, or did you envision groups of users?task_areas_geojson == {}): this PR shows a "split first" callout rather than materialising a synthetic single task — OK?Refs #3095. Related #3064.