Map Locations to Entity List Attribute#47
Conversation
…when fetching farm data from ODK central entity list
… sync to ODK central entity list
|
Progress update:
TODO:
|
…utes loc1 (cluster id) and loc2 (group id)
|
Just applied a fix to find and fill in value for attributes loc1 (cluster ID) and loc2 (group ID). To show how it works, I will refresh my local database then do testings with screen shots tomorrow morning. |
|
Here is the excel files for my testing: Groups excel file Import Farm List Import Locations and Farm List Morning @dave-mills - I think it is now ready for review. |
dave-mills
left a comment
There was a problem hiding this comment.
The main point here is that we cannot be hardcoding location values. It looks like this is leaning heavily on the idea that we have "Group" and "Cluster" as the only 2 levels, (and using an old team's data as the canonical dataset). A team can have any number of location levels, and will come with their own locations - we do not know them in advance.
Second point - not related specifically to locations
I also noticed an issue in the main Farm ODK Entity handling that only arose because I used a 2nd team for testing this time around without refreshing the database. Because the teams share a "farm_entities" dataset, which tracks the variables already added, I got an error when pushing the farms to my new team's ODK EntityList:
"HTTP request returned status code 400: {"message":"The entity is invalid. You specified the dataset property [label_farm_entities] which does not exist." ...
This is caused because in reconcileProperties(), existing variables are not explicitly pushed to ODK Central as new properties. But they are new properties for the new team. I can see a few possible fixes to this:
- always push the variables to ODK Central (quite wasteful; after the first time for each team it'll just return 409 (already exists) and quietly move on).
- catch the 400 error thrown above, and then try to push the variables.
- make the "farm_entities" dataset in our database team-owned, so each team has their own set of farm dataset_variables.
I think option 3 is the closest to the main intent - and also makes sense as different teams may have different farm properties.
| $pos = $current->locationLevel->pos; | ||
| $attributes["loc{$pos}"] = '1'; | ||
| $attributes["loc{$pos}_name"] = (string) $current->name; | ||
| $attributes["loc{$pos}_type"] = "Loc{$pos} name"; |
There was a problem hiding this comment.
loc{pos}_type should be the type of location, e.g. "Group", "Cluster", "Village" etc.
There was a problem hiding this comment.
(I suspect it was in the "Loc{pos} name" format because the example csv files were created without full knowledge of how the location levels system works)
| // Hardcoded internal group ID / internal cluster ID per group name, keyed by the exact | ||
| // group name (lookup is case-insensitive - see resolveGroupClusterIds()). These specific | ||
| // IDs are defined in the ODK form's own choice list (used for Enketo's cascading | ||
| // selects) - they cannot be derived from this app's own Location/LocationLevel records, | ||
| // so per Dan (2026-07-15) they're hardcoded here instead. A group not in this table | ||
| // (e.g. a different team's data) simply falls back to the generic '1' placeholder in | ||
| // buildLocationAttributes() - see docs/plans/map-loc-attributes-to-location-levels.md. | ||
| // | ||
| // @var array<string, array{groupId: int, clusterId: int}> | ||
| protected const GROUP_CLUSTER_LOOKUP = [ |
There was a problem hiding this comment.
We shouldn't be hardcoding any location data!
| * `loc{n}` itself (per Dan, 2026-07-15): the given Location (the farm's own attachment | ||
| * point, i.e. the "Group") and its immediate parent (the "Cluster") get their real | ||
| * internal group ID / internal cluster ID from GROUP_CLUSTER_LOOKUP, matched by the | ||
| * Group's name - these specific IDs matter for Central-side cascading selects to place | ||
| * the farm under the correct cluster/group, unlike every other level's `loc{n}`, which | ||
| * stays a fixed placeholder value `"1"` (only its presence signals "this level has | ||
| * data" - see the 2026-07-13 note in git history for why that was fine before this). | ||
| * A Group name not found in the lookup (e.g. a different team's data) falls back to the | ||
| * same `"1"` placeholder for both loc{n} positions. |
There was a problem hiding this comment.
I don't understand this. What happens when a team registers and has 3 location levels: "District", "Sub-district" and "village"?
The whole point of this work is so that teams can manage the locations and location groups in one central location - the platform. It should be synchronised to the ODK form via the helper function we looked at together - the one that regenerates the entire "Locations" module based on the team's location levels and locations. Note, for that to happen:
|
|
@dan-tang-ssd - I'll take a quick look at the ODK "Locations" module generator now, and share a small PR into this branch. |
Replaces the legacy combined LocationSectionBuilder with two focused builders, split to match the new separate-farm-registration workflow.
Breaks the approved design (location-and-farm-info-module-rewrite.md) into 4 TDD tasks: can_be_replaced support in Xlsform::syncWithTemplate(), the two new builder classes, and wiring Team::localiseXlsforms() to them.
Captures operational gaps and local-DB findings from planning that aren't part of any task's code but matter for rollout: the dormant has_updated_locations flag, the has_latest_template resync gotcha, the current local dev DB template/module snapshot, and the jr:choice-name source citations.
…t. Resolve it by making dataset team specific
|
Some changes made:
For testing, I refreshed my local database with migration and seeders. I created different location levels for different teams:
I imported locations by uploading excel file. For attributes loc1, loc2, loc3, loc4, etc. I put value of column locations.code for testing now. I think the program changes is in correct direction now. Screen shots: Team 1 - Location Levels, Locations, Farms, Farm Entities in ODK Central (one farm created by submitted Farm Registration form, one farm imported in application)
Team 2 - Location Levels, Locations, Farms, Farm Entities in ODK Central
Team 3 - Location Levels, Locations, Farms, Farm Entities in ODK Central
|
Location odk module builder
|
After testing, locations levels data can be retrieved from database to Xlsform. Farm info data can be retrieved from entity list. I think it is now ready for review. |
|
@dan-tang-ssd - thanks. Good find on the cross-team scoping problem. This is now fixed with specific team scoping in the locations import class when finding the "parent" location. |
- LocationModuleBuilder now deletes choice list entries for deleted locations - saved and deleted hooks for FarmEntity now flag forms with draft_needs_update - deleted locations also flag forms with draft_needs_update - added extra tests for locations and farms module builder; - module builders now build 1 local module version per form (i.e. per XlsformModule entry), rather than a single one; to avoid problems of resyncing with Xlsform syncFromTemplate()




































































This PR is submitted to fix #46
It is not yet ready for review. It is submitted for progress update.
Expected workflow:
It contains below items: