Repository navigation
Conversation
…o the api for the moment
…m in multiple projects
There was a problem hiding this comment.
general comments:
- i made a few comments to improve readability, otherwise backend looks good
- i don't really understand what these mappings are, so some comments i made below might be dumb
testing:
- the "how to test" section might need a bit more explanations, because i was lost
- comparing a mapping with itself shows that all quesitons are dropped, i would expect everything to be shown in "identical"
- when checking a mapping and clicking on a question on the left side, i get an error screen
it looks like each click calls our API, which calls the DHIS2 API and there's a configuration issue on my account - we should probably get a better error message in such cases
ERROR 2026-10-07 10:56:55,656 django.request -- Internal Server Error: /api/datasources/78/dataElements.json
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://play.im.dhis2.org/stable-2-41-9/api/dataElements.json?fields=id%2Cname%2CvalueType%2CdomainType%2CoptionSet%5Boptions%5Bid%2Cname%2Ccode%5D%5D%2CcategoryCombo%5Bid%2Cname%2CcategoryOptionCombos%5Bid%2Cname%5D%5D%2CdataSetElements%5BdataSet%5Bid%2Cname%2CperiodType%5D%5D&pageSize=10&filter=code%3Ailike%3Aimei
i'll let somebody else review the frontend
|
|
||
|
|
||
| class MappingVersionQuerySet(models.QuerySet): | ||
| def filter_for_user(self, user: User): |
There was a problem hiding this comment.
i would probably add a unit test at the model level to make sure you get the expected results from this
|
|
||
|
|
||
| def visit_mappable(node, form_descriptor, mappable_questions): | ||
| if node.get("type") not in NOT_MAPPABLE_TYPES and "name" in node: |
There was a problem hiding this comment.
i would make local variables for node_type and node_name
| mapping_version.form_version = form_version | ||
|
|
||
| questions_by_name = form_version.questions_by_name() | ||
| # unlike questions_by_name, also has the questions inside repeat groups |
There was a problem hiding this comment.
do you still need questions_by_name then?
| SELECT_MULTIPLE_TYPE = "select all that apply" | ||
|
|
||
|
|
||
| def _choices(node, form_descriptor): |
There was a problem hiding this comment.
i would also rename this, i don't get why it's called _choices but returns children nodes
| from .common import HasPermission, ModelViewSet, TimestampField | ||
|
|
||
|
|
||
| def get_question_mapping_shape_error(mapping_type, data_element): |
There was a problem hiding this comment.
nitpick: i would probably add unit tests for this function and test it without any API call, but tests with API calls work too
| if data_element is not None and not is_unmap: | ||
| shape_error = get_question_mapping_shape_error(instance.mapping.mapping_type, data_element) | ||
| if shape_error: | ||
| raise serializers.ValidationError({path: shape_error}) |
There was a problem hiding this comment.
maybe we should store errors in a list, then check outside the for question_name, data_element loop if the list has errors, in case there are multiple errors in the same question_mappings? i'm not sure if that scenario is possible
| self.assertJSONResponse(resp, status.HTTP_200_OK) | ||
| self.assertEqual(len(resp.json()["mapping_versions"]), 0) |
There was a problem hiding this comment.
same suggestion for all calls
| self.assertJSONResponse(resp, status.HTTP_200_OK) | |
| self.assertEqual(len(resp.json()["mapping_versions"]), 0) | |
| result = self.assertJSONResponse(resp, status.HTTP_200_OK) | |
| self.assertEqual(len(result["mapping_versions"]), 0) |
There was a problem hiding this comment.
went a step further
def assertMappingVersionsCount(self, response, expected_count):
result = self.assertJSONResponse(response, status.HTTP_200_OK)
self.assertEqual(len(result["mapping_versions"]), expected_count)
| self.assertEqual(self.get_question_mappings(mapping_version_id), original) | ||
| self.assertEqual(Modification.objects.filter(object_id=mapping_version_id).count(), 1) | ||
|
|
||
| def test_mappingversions_patch_logs_modification(self): |
|
the 500 / http error looks more the datasource or dhis2 wasn't available anymore (perhaps play dhis2 url version increment ?) or you seeded the account right before testing ? thanks, I'll have a look at the comments |
… first) and it's coverage
…mapping, but that's ok
|
so from your bad experience during testing
|
| }); | ||
| return result; | ||
| }, [rows]); | ||
| const bucketRows = rows.filter(row => row.kind === bucket); |
| mappingVersionId: mappingVersion.id, | ||
| questionMappings: plan.changes, | ||
| }); | ||
| openSnackBar( |
There was a problem hiding this comment.
Why not using the bulkUpdate snack query for this?
There was a problem hiding this comment.
Ah, I get it, it doesn't allow to use replacements.
Could we log a task for this ?
|
|
||
| {currentMappingVersion && ( | ||
| <Box className={classes.containerFullHeightNoTabPadded}> | ||
| <Box className={classes.actions}> |
There was a problem hiding this comment.
I see we still use className with classes here.
If not to much of work, I would change it to use SX in this file.
| }; | ||
| rows.forEach(row => { | ||
| const decision = decisions[row.questionKey]; | ||
| if (row.kind === 'dropped') { |
There was a problem hiding this comment.
Would be nice to have all kinds in an enum
| export class MappingImportError extends Error { | ||
| constructor( | ||
| public reason: | ||
| | 'invalidJson' |
There was a problem hiding this comment.
Same here, to prevent typos and improve reusability of this list of invalid errors


What problem is this PR solving?
Allow to export/import/copy dhis2 mappings from another form version (or form or server)
Related JIRA tickets
IA-5375
Changes
How to test
The rest of test is playing with same features
Print screen / video
2 new buttons at the top
then a "big" wizard/dialog to pick from another version or import from a file
a first step to pick the mapping
then decide for each questions, if you want to take it or not (especially if a question was mapped in current and to import mapping)
Notes
Doc
Tell us where the doc can be found (docs folder, wiki, in the code...).