There are a number of advanced options for organization levels. These are shown when you click on the Advanced checkbox on the Organization Levels page.
Type Code
This is a code that you can use to refer to the type when accessing locations in lookup tables or logic. For example, if your level is called District Warehouse, the type code might be district_warehouse
Level to Expand From
By default, CommCare will only send each user the list of their assigned locations and those location's ancestors and descendants. For example, if the organization levels are State -> District -> Block -> Outlet, and the user is assigned to a block, they will get a list of their state, their district, their block and all that block's outlets. In some situations, you may want to get a list of locations outside of the ancestors and descendants. In the example above, if a user is assigned to a block, they may want to see all blocks in their district anyway. This is controlled using the "Expand From" option.
This option sets the level below which all locations will be synced down. For example, if the user is assigned to a block but the level to expand from is set to "district", that user will get a list of their state, their district, and all blocks in that district (including their own).
Level to Expand To
This option should only be changed if you need to improve your application's performance.
This option limits the types of locations that are sent to a given user. For example, if the organization levels are State -> District -> Block -> Outlet, and the user is assigned to a district, they will get a list of their state, their district and all blocks and all outlets in that block. If there are too many outlets and the list of outlets is not needed in the application, the level to expand to can be set to "block". Commcare will then not send any locations below the block level.
Force Sync
This option allows you to force CommCare to send all locations of a given type, but not any of those locations descendants. For example, if the organization levels are State -> District -> Block -> Outlet, and the user is assigned to a block, they will get a list of their state, their district, their block and all that block's outlets. In some situations, you may want to get a list of states as well (but none of the state's districts, blocks or outlets). You can achieve this by setting the Force Sync option to "State".
Translating or Customizing Location Display
Location names are not directly translatable. This document describes an approach to work around this limitation. It allows you to arbitrarily customize the display of locations or any other fixture in your app.
Multiple choice or checkbox lookup table questions typically operate on a fixture instance, but you don't have very much control. The technique described here instead creates a "nodeset" using a repeat group with a model iteration query, and then uses that for the lookup table question. This way, you can define whatever properties you need in the nodeset for reference in the lookup table question.
Storing Location Translations
The first thing you need for this to work is stored translations. You can define custom location fields for the name in each supported language, for example:
name-en
name-hin
name-es
Then edit your locations to populate these fields. This information will now be available to access in your application.
Referencing the current language
This process is described in the page on https://dimagi.atlassian.net/wiki/x/7CTKfw. If you set up the lang-code question as described there, you should be able to use jr:itext('lang-code-label') to refer to the current language.
Setting up a nodeset
Add a new repeat group, and under the "Advanced" section, set the Model Iteration ID Query to "instance('locations')/locations/location/@id". This will make one iteration of the repeat group for each location in your project. You can add a filter here if you like, or you can filter the expression later, in your lookup table question. Set the Instance ID to "locations", and the Instance URI to "jr://fixture/locations" to make sure the instance is available in the form.
Inside this repeat group, add hidden values for whatever you may need to reference. The location id will be made available by virtue of the Model Iteration ID Query. Here you can make a translated "name" field - just add a hidden value which selects the appropriate field for the active language:
cond(jr:itext('lang-code-label') = 'es', instance('locations')/locations/location[@id = ../@id]/location_data/name-es,
jr:itext('lang-code-label') = 'en', instance('locations')/locations/location[@id = ../@id]/location_data/name-en,
jr:itext('lang-code-label') = 'hin', instance('locations')/locations/location[@id = ../@id]/location_data/name-hin,
instance('locations')/locations/location[@id = ../@id]/name)
Note that you could do whatever you wanted here to decide how to display the location - you could reference the location's state, or a calculated property elsewhere in the form, for instance.
Setting up the Lookup Table question
Now that you have a suitable nodeset, you can set up a lookup table to reference it. Add a new lookup table question and edit the lookup table. Drag the repeat group used to make your nodeset into the "Query Expression" field. It should look something like "/data/locations/item" (*Note: it must start with "/data/". The #form/ syntax will throw an error.) Leave the Instance ID and Instance URI fields blank. Then set the Value Field to "@id", and the Display Text Field to "name", or whatever field in your nodeset you want to display. That's it! Your lookup table question should now use the nodeset described above, with translated names.
Referencing the Location/Organization Hierarchy in Applications
Please note the documentation below requires more technical expertise
The documentation below may require knowledge of more technical software management and development topics.
This page discusses how to reference the location hierarchy in various parts of apps.
Enabling Access to Location Hierarchy
All new projects have the location hierarchy.
Some older projects may be setup to only receive the Old Hierarchical Fixture. To transition to the flat fixture, go to project settings, then choose Pre-Release Features -> Location Fixture. You can use this to turn on the new location format (flat fixture). Once you have done this, new versions of the app will start to use the new location format. Once all your existing users have transitioned to a new version of the application that uses the new location format, turn off the hierachical fixture. This process is described in depth at https://dimagi.atlassian.net/wiki/x/cinKfw.
The User's location
To pull the current user's location ID you can use the "commcare_location_id" session variable. To reference this in a form, use the following expression:
instance('commcaresession')/session/user/data/commcare_location_id
The Location Hierarchy
To reference the location hierarchy in forms you need edit the source XML by hand and construct complicated xpath expressions.
Ensure that the Locations Data Source is Available
First, you'll need to make sure that the form has a reference to the Locations data source.
The easiest way to accomplish this is to ensure that your question has at least one select question with locations as choices.
If your form doesn't contain one of those questions you can create a 'dummy' question:
Below you'll see instructions for "Making select questions with locations as choices"
Follow those instructions to add a locations lookup to the form
Set the Display Condition for that question to false()
After those steps you should be able to reference locations elsewhere in the form.
Note: If you are using a Locations driven select question in the form, you won't need to keep around the 'dummy' question as well.
Referencing the properties of the user's location (or location ancestor) in a form
You can use the following templates to reference the properties of the user's location (or ancestors). These examples assume a hierarchy of state --> district --> block --> outlet. These expressions will need to be customized to your own hierarchy. To find the code to use for each organization level, on the Organization Level page, click on the Advanced checkbox.
For example, if my location hierarchy has village → city → county→ and I'm trying to pull the city's ID for my current village user, I would take this example from below:
instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@block_id
and modify @block_id to say @city_id
instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@city_id
I am a... | referencing my... | syntax in forms |
outlet | own location's name | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/name |
outlet | own location's type | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@type |
outlet | own location's site code | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/site_code |
outlet | own location's custom data | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/location_data/custom_field_id |
outlet | parent block's id | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@block_id |
outlet | parent block's name | instance('locations')/locations/location[@id = instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@block_id]/name |
outlet | parent block's name | instance('locations')/locations/location[@id = instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@block_id]/site_code |
outlet | parent district's id | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@district_id |
outlet | parent district's name | instance('locations')/locations/location[@id = instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@district_id]/name |
block | parent district's id | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@district_id |
block | parent district's name | instance('locations')/locations/location[@id = instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@district_id]/name |
district | parent state's id | instance('locations')/locations/location[@id = instance('commcaresession')/session/user/data/commcare_location_id]/@state_id |
Accessing the location additional information (Location Fields)
Once you have the location's ID you can access it to get other information.
These examples assume your ID is stored in a hidden value called location_id
Referencing... | syntax in forms |
---|---|
The gps of the location | instance('locations')/locations/location[@id = /data/location_id]/latitude instance('locations')/locations/location[@id = /data/location_id]/longitude |
Any custom location fields you may have (ex. admin name) | instance('locations')/locations/location[@id = /data/location_id]/location_data/admin_name |
Making select questions with locations as choices
You can use the location hierarchy to make select questions that allow you to choose a location in a form. The process is similar to setting up multiple choice questions with cases as choices.
Go to Application Settings to turn on the Custom Single and Multiple Answer Add-On in the "Calculations" section.
Add a new "multiple choice lookup table" question
In the "lookup table data" choose the 3 dots next to the list of lookup tables
Set the query to: instance('locations')/locations/location[@type = 'TYPE OF LOCATION'] NOTE: "type of location" is given for each organization level by the "type code." You can see the type code when you click on "Advanced Mode" on the Organization Levels page.
Set the instance ID to locations
Set the instance URI to jr://fixture/locations
Hit save
Set the value to "@id" and the label to "name"
Tiered Location Selection
You can also add tiered selection (e.g. select state, then district, then block) by adding multiple select questions after each other as described above. Assuming the example of districts and block:
Setup the district list as described above (this example assumes the question_id is district)
Add a second multiple choice question, and configure it to display a list of blocks
In the filter section, add a filter with the following: @district_id = /data/district
Example location fixture structure
Example locations fixture
<fixture id="locations" indexed="true" user_id="b43e50ca1c3174fe0bbcf36cfd93a1eb"> <locations> <location id="999dc78d9ca84ea8749dc75b44542332" type="country" country_id="999dc78d9ca84ea8749dc75b44542332" state_id="" county_id="" city_id=""> <name>USA</name> <site_code>usa</site_code> <external_id/> <latitude/> <longitude/> <location_type>country</location_type> <supply_point_id/> <location_data> <name-en>USA</name-en> <name-es>USA</name-es> <is_test/> </location_data> </location> <location id="999dc78d9ca84ea8749dc75b44541bdc" type="state" country_id="999dc78d9ca84ea8749dc75b44542332" state_id="999dc78d9ca84ea8749dc75b44541bdc" county_id="" city_id=""> <name>Massachusetts</name> <site_code>massachusetts</site_code> <external_id/> <latitude/> <longitude/> <location_type>state</location_type> <supply_point_id/> <location_data> <is_test/> </location_data> </location> <location id="999dc78d9ca84ea8749dc75b44540257" type="county" country_id="999dc78d9ca84ea8749dc75b44542332" state_id="999dc78d9ca84ea8749dc75b44541bdc" county_id="999dc78d9ca84ea8749dc75b44540257" city_id=""> <name>Middlesex</name> <site_code>middlesex</site_code> <external_id/> <latitude/> <longitude/> <location_type>county</location_type> <supply_point_id/> <location_data> <name-en>Middlesex</name-en> <name-es>Middlesex</name-es> <is_test/> </location_data> </location> <location id="999dc78d9ca84ea8749dc75b4453ff5b" type="city" country_id="999dc78d9ca84ea8749dc75b44542332" state_id="999dc78d9ca84ea8749dc75b44541bdc" county_id="999dc78d9ca84ea8749dc75b44540257" city_id="999dc78d9ca84ea8749dc75b4453ff5b"> <name>Cambridge</name> <site_code>cambridge</site_code> <external_id/> <latitude/> <longitude/> <location_type>city</location_type> <supply_point_id/> <location_data> <is_test/> </location_data> </location> </locations> </fixture>
Syncing more of location hierarchy to users
By default, CommCare will only send each user the list of their assigned locations and those location's ancestors and descendants. For example, if I'm assigned to a block, I will get a list of my state, my district, my block and all that blocks outlets.
In some situations, you may want to get a list of locations outside of the ancestors and descendants. In the example above, if a user is assigned to a block, they may want to see all districts in that state anyway. This is controlled using the "Expand From" options outlined on https://dimagi.atlassian.net/wiki/x/GW-Kfw.
Migrating your project from the hierarchical location fixture to the flat location fixture.
Why might you want to do this? The flat fixture is much simpler to use and understand, and it's faster!
I'm making a new domain and want to use the flat fixture
We got you covered, all new domains use the flat fixture by default.
I have an existing domain that I want to upgrade to the flat fixture
Go to "Project Settings" then on the left, under "Pre-Release Features" click "Location Fixture". If you don't see this link, you're probably already on the flat fixture. That page lets you configure which version to send to the phone. If the flat fixture is enabled here, your forms will automatically try to reference the flat fixture in new builds of your app.
We expect that the migration will go something like this:
Starting off, you may have an existing app which references the old fixture, and this app may have active users. The idea is to be able to migrate your app without disrupting active users.
Go to the aforementioned "Location Fixture" page and check "Sync the flat location fixture". Do not uncheck the other box yet.
Begin converting your app to use the flat fixture as described on https://dimagi.atlassian.net/wiki/x/DArKfw. You can make new app versions to a phone and test them out, and your existing users won't be affected (As usual, don't release any new builds until it's working so your users don't accidentally update).
Once you're confident that it's working, star the new app version and get your users to update.
Once none of your users are using the old app version, you can uncheck "Sync the hierarchical location fixture" to reduce the size of the restore payload that must be synced to the phones.
I have an existing domain with multiple apps that I want to upgrade to the flat fixture incrementally
Go to "Project Settings" then on the left, under "Pre-Release Features" click "Location Fixture".
Ensure that the option "Sync the hierarchicial location fixture (legacy format)." is already selected. If it is not selected, then you are already on the flat fixture format.
On the application that you want to upgrade, go to the applications settings page by selecting the gear in the app builder.
Go to the "Advanced Settings" tab. Under the "Advanced" section select "Both Hierarchical and Flat Fixture" from the "App Aware Location Fixture Format" dropdown. This will allow you to reference both the hierarchical fixture and flat fixture in this app.
To reference both fixtures you need to add the instance references in your form appropriately. In your form that references them both you must add:
<instance src="jr://fixture/locations" id="locations" />
<instance src="jr://commtrack:locations" id="locations_old" />
When writing your xpath references, to refer to the new flat file you use "instance('locations')" to refer to the old format you can use "instance('locations_old')"
Note: you can put anything you want in the id= section above. That is how you'll refer to it in your app.
Begin converting your app to use the flat fixture as described on https://dimagi.atlassian.net/wiki/x/DArKfw. You can make new app versions and publish them to a phone and test them out, and your existing users won't be affected (As usual, don't release any new builds until it's working so your users don't accidentally update).
Once you're confident that it's working and all location references have been converted to the flat fixture, select the "Only Flat Fixture" from the "App Aware Location Fixture Format" dropdown. This will disallow any references to the hierarchical fixture in this app.
Test your application again with this option selected.
Once you're confident that it's working, release a new app version and get your users to update to the new version.