def post(self, request, *args, **kwargs): upload = request.FILES.get('bulk_upload_file') if not upload: messages.error(request, _('no file uploaded')) return self.get(request, *args, **kwargs) if not args: messages.error(request, _('no domain specified')) return self.get(request, *args, **kwargs) if not upload.name.endswith('.xlsx'): messages.error(request, _("Invalid file-format. Please upload a valid xlsx file.")) return self.get(request, *args, **kwargs) try: get_workbook(upload) except WorkbookJSONError as e: messages.error(request, e) return self.get(request, *args, **kwargs) domain = args[0] ref = self._cache_file(request, domain, upload) if not isinstance(ref, LocationImportView.Ref): # ref is HTTP response: lock could not be acquired return ref file_ref = ref.value task = import_locations_async.delay(domain, file_ref.download_id, request.couch_user.user_id) file_ref.set_task(task) return HttpResponseRedirect( reverse( LocationImportStatusView.urlname, args=[domain, file_ref.download_id] ) )
def upload_bulk_app_translations(request, domain, app_id): lang = request.POST.get('language') validate = request.POST.get('validate') app = get_app(domain, app_id) workbook = None msgs = [] try: workbook = get_workbook(request.file) except WorkbookJSONError as e: messages.error(request, six.text_type(e)) if workbook: if validate: msgs = validate_bulk_app_translation_upload(app, workbook, request.user.email, lang) else: headers = get_bulk_app_sheet_headers(app, lang=lang) msgs = process_bulk_app_translation_upload(app, workbook, headers, lang=lang) app.save() for msg in msgs: # Add the messages to the request object. # msg[0] should be a function like django.contrib.messages.error . # msg[1] should be a string. msg[0](request, msg[1]) # In v2, languages is the default tab on the settings page return HttpResponseRedirect( reverse('app_settings', args=[domain, app_id]) )
def upload_bulk_app_translations(request, domain, app_id): lang = request.POST.get('language') validate = request.POST.get('validate') app = get_app(domain, app_id) try: workbook = get_workbook(request.file) except WorkbookJSONError as e: messages.error(request, str(e)) else: if validate: if not lang: msgs = [(messages.error, _("Please select language to validate."))] else: msgs = validate_bulk_app_translation_upload( app, workbook, request.user.email, lang) else: sheet_name_to_unique_id = get_sheet_name_to_unique_id_map( request.file, lang) msgs = process_bulk_app_translation_upload(app, workbook, sheet_name_to_unique_id, lang=lang) app.save() _add_messages_to_request(request, msgs) # In v2, languages is the default tab on the settings page return HttpResponseRedirect(reverse('app_settings', args=[domain, app_id]))
def test_upload(self): headers = (("translations", ("id", "name", "case_type")), ) data = (("translations", ( (self.translated_rule.id, 'test updated', 'song'), (self.email_rule.id, 'test email', 'song'), (1000, 'Not a rule', 'person'), )), ) file = BytesIO() export_raw(headers, data, file, format=Format.XLS_2007) with tempfile.TemporaryFile(suffix='.xlsx') as f: f.write(file.getvalue()) f.seek(0) workbook = get_workbook(f) msgs = [ m[1] for m in upload_conditional_alert_rows( self.domain, workbook.get_worksheet()) ] self.assertEqual(len(msgs), 3) self._assertPatternIn( r"Row 3, with rule id \d+, does not use SMS content", msgs) self._assertPatternIn( r"Could not find rule for row 4, with id \d+", msgs) self.assertIn("Updated 1 rule(s)", msgs) self.assertEqual(self.translated_rule.name, 'test updated') self.assertEqual(self.translated_rule.case_type, 'song') self.assertEqual(self.untranslated_rule.name, 'test') self.assertEqual(self.untranslated_rule.case_type, 'person')
def uploaded_file(self): try: bulk_file = self.request.FILES['bulk_upload_file'] except KeyError: raise BulkUploadCasesException(_("No files uploaded")) try: return get_workbook(bulk_file) except WorkbookJSONError as e: raise BulkUploadCasesException(str(e))
def uploaded_file(self): try: bulk_file = self.request.FILES['bulk_upload_file'] except KeyError: raise BulkUploadCasesException(_("No files uploaded")) try: return get_workbook(bulk_file) except WorkbookJSONError as e: raise BulkUploadCasesException(six.text_type(e))
def post(self, request, *args, **kwargs): try: workbook = get_workbook(request.FILES['bulk_upload_file']) except WorkbookJSONError as e: messages.error(request, str(e)) return self.get(request, *args, **kwargs) msgs = upload_conditional_alert_workbook(self.domain, workbook) for msg in msgs: msg[0](request, msg[1]) return self.get(request, *args, **kwargs)
def test_validate_parents(self, location_type_mock, locations_mock, *_): location_type_mock.by_domain.return_value = self.location_types location_type_mock.select_related.return_value.filter.return_value = self.location_types locations_mock.select_related.return_value.filter.return_value = [ Location(site_code='13', location_type=self.state_location_type) ] with tempfile.TemporaryFile() as file: export_raw(self.headers, self.rows, file, format=Format.XLS_2007) file.seek(0) workbook = get_workbook(file) parser = Parser(self.domain, workbook) errors = parser.parse() self.assertIn('Unexpected non-state parent 1 set for supervisor', errors, "missing location found") self.assertIn('Unexpected state parent 13 set for awc', errors, "incorrect parent type not flagged") self.assertIn('Parent 12 is marked for archival', errors, "archived parent not caught")
def test_validate_new_site_codes_type(self, location_type_mock, locations_mock, *_): location_type_mock.by_domain.return_value = self.location_types location_type_mock.select_related.return_value.filter.return_value = self.location_types locations_mock.select_related.return_value.filter.return_value = [ Location(site_code='13', location_type=self.state_location_type) ] with tempfile.TemporaryFile() as file: export_raw(self.headers, self.rows, file, format=Format.XLS_2007) file.seek(0) workbook = get_workbook(file) parser = Parser(self.domain, workbook) errors = parser.parse() self.assertIn('state 13 used as supervisor', errors) self.assertIn('state 13 used as awc', errors) self.assertNotIn('state 13 used as state', errors)
def _upload(self, data, headers=None): if headers is None: headers = ( ("translated", ("id", "name", "message_en", "message_es")), ("not translated", ("id", "name", "message")), ) file = BytesIO() export_raw(headers, data, file, format=Format.XLS_2007) with tempfile.TemporaryFile(suffix='.xlsx') as f: f.write(file.getvalue()) f.seek(0) workbook = get_workbook(f) msgs = upload_conditional_alert_workbook(self.domain, workbook) return [m[1] for m in msgs] # msgs is tuples of (type, message); ignore the type
def process_ui_translation_upload(app, trans_file): trans_dict = defaultdict(dict) # Use this to hard fail and not update any translations error_properties = [] # Use this to pass warnings without failing hard warnings = [] try: workbook = get_workbook(trans_file) except WorkbookJSONError as e: error_properties.append(six.text_type(e)) return trans_dict, error_properties, warnings commcare_version = get_commcare_version_from_workbook(workbook.wb) try: translations = workbook.get_worksheet(title='translations') except WorksheetNotFound: error_properties.append( _('Could not find sheet "translations" in uploaded file.')) return trans_dict, error_properties, warnings commcare_ui_strings = list( load_translations('en', version=2, commcare_version=commcare_version).keys()) default_trans = get_default_translations_for_download( app, commcare_version) lang_with_defaults = app.langs[get_index_for_defaults(app.langs)] for row in translations: if row["property"] not in commcare_ui_strings: # Add a warning for unknown properties, but still add them to the translation dict warnings.append( row["property"] + " is not a known CommCare UI string, but we added it anyway") for lang in app.langs: if row.get(lang): all_parameters = re.findall(r"\$.*?}", row[lang]) for param in all_parameters: if not re.match(r"\$\{[0-9]+}", param): error_properties.append(row["property"] + ' - ' + row[lang]) if not (lang_with_defaults == lang and row[lang] == default_trans.get(row["property"], "")): trans_dict[lang].update({row["property"]: row[lang]}) return trans_dict, error_properties, warnings
def post(self, request, *args, **kwargs): """View's dispatch method automatically calls this""" try: self.workbook = get_workbook(request.FILES.get('bulk_upload_file')) except WorkbookJSONError as e: messages.error(request, str(e)) return self.get(request, *args, **kwargs) try: self.user_specs = self.workbook.get_worksheet(title='users') except WorksheetNotFound: try: self.user_specs = self.workbook.get_worksheet() except WorksheetNotFound: return HttpResponseBadRequest("Workbook has no worksheets") try: self.group_specs = self.workbook.get_worksheet(title='groups') except WorksheetNotFound: self.group_specs = [] try: check_headers(self.user_specs) except UserUploadError as e: messages.error(request, _(str(e))) return HttpResponseRedirect( reverse(UploadCommCareUsers.urlname, args=[self.domain])) task_ref = expose_cached_download(payload=None, expiry=1 * 60 * 60, file_extension=None) task = import_users_and_groups.delay( self.domain, list(self.user_specs), list(self.group_specs), ) task_ref.set_task(task) return HttpResponseRedirect( reverse(UserUploadStatusView.urlname, args=[self.domain, task_ref.download_id]))
def test_parser(self, location_type_mock, _): type_codes = ['state', 'supervisor', 'awc'] location_type_mock.return_value = list( map(lambda site_code: LocationType(code=site_code), type_codes)) with tempfile.TemporaryFile() as file: export_raw(self.headers, self.rows, file, format=Format.XLS_2007) file.seek(0) workbook = get_workbook(file) parser = Parser(self.domain, workbook) errors = parser.parse() self.assertEqual(parser.valid_transitions['awc']['Move'], {'131': '112'}) self.assertEqual(parser.valid_transitions['awc']['Merge'], {'132': ['113', '114']}) self.assertEqual(parser.valid_transitions['supervisor']['Move'], {'13': '12'}) self.assertEqual(errors, [ "No change in location code for Extract, got old: '111' and new: '111'", "New location 132 reused with different information", "Missing location code for Split, got old: '11' and new: ''", "Invalid Operation Unknown" ])
def process_ui_translation_upload(app, trans_file): trans_dict = defaultdict(dict) # Use this to hard fail and not update any translations error_properties = [] # Use this to pass warnings without failing hard warnings = [] try: workbook = get_workbook(trans_file) except WorkbookJSONError as e: error_properties.append(six.text_type(e)) return trans_dict, error_properties, warnings commcare_version = get_commcare_version_from_workbook(workbook.wb) try: translations = workbook.get_worksheet(title='translations') except WorksheetNotFound: error_properties.append(_('Could not find sheet "translations" in uploaded file.')) return trans_dict, error_properties, warnings commcare_ui_strings = list(load_translations('en', version=2, commcare_version=commcare_version).keys()) default_trans = get_default_translations_for_download(app, commcare_version) lang_with_defaults = app.langs[get_index_for_defaults(app.langs)] for row in translations: if row["property"] not in commcare_ui_strings: # Add a warning for unknown properties, but still add them to the translation dict warnings.append(row["property"] + " is not a known CommCare UI string, but we added it anyway") for lang in app.langs: if row.get(lang): all_parameters = re.findall(r"\$.*?}", row[lang]) for param in all_parameters: if not re.match(r"\$\{[0-9]+}", param): error_properties.append(row["property"] + ' - ' + row[lang]) if not (lang_with_defaults == lang and row[lang] == default_trans.get(row["property"], "")): trans_dict[lang].update({row["property"]: row[lang]}) return trans_dict, error_properties, warnings
def test_get_workbook(self): formula_filepath = self.get_path('formula_sheet', 'xlsx') workbook = get_workbook(formula_filepath) self.assertEquals(len(workbook.worksheets), 1) self.assertListEqual([row['name'] for row in workbook.worksheets[0]], ['ben'])
def test_get_workbook_duplicate_columns(self): bad_filepath = self.get_path('duplicate_columns', 'xlsx') with self.assertRaises(WorkbookJSONError): get_workbook(bad_filepath)
def test_get_workbook_bad_file(self): bad_filepath = self.get_path('not_excel_file', 'xlsx') with self.assertRaises(WorkbookJSONError): get_workbook(bad_filepath)
def test_get_workbook(self): formula_filepath = self.get_path('formula_sheet', 'xlsx') workbook = get_workbook(formula_filepath) self.assertEqual(len(workbook.worksheets), 1) self.assertListEqual([row['name'] for row in workbook.worksheets[0]], ['ben'])
def test_get_workbook_duplicate_columns(self): bad_filepath = self.get_path('duplicate_columns', 'xlsx') with self.assertRaises(WorkbookJSONError): get_workbook(bad_filepath)
def post(self, request, *args, **kwargs): """View's dispatch method automatically calls this""" try: self.workbook = get_workbook(request.FILES.get('bulk_upload_file')) except WorkbookJSONError as e: messages.error(request, six.text_type(e)) return self.get(request, *args, **kwargs) try: self.user_specs = self.workbook.get_worksheet(title='users') except WorksheetNotFound: try: self.user_specs = self.workbook.get_worksheet() except WorksheetNotFound: return HttpResponseBadRequest("Workbook has no worksheets") try: self.group_specs = self.workbook.get_worksheet(title='groups') except WorksheetNotFound: self.group_specs = [] try: check_headers(self.user_specs) except UserUploadError as e: messages.error(request, _(six.text_type(e))) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) # convert to list here because iterator destroys the row once it has # been read the first time self.user_specs = list(self.user_specs) for user_spec in self.user_specs: try: user_spec['username'] = enforce_string_type(user_spec['username']) except StringTypeRequiredError: messages.error( request, _("Error: Expected username to be a Text type for username {0}") .format(user_spec['username']) ) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) try: check_existing_usernames(self.user_specs, self.domain) except UserUploadError as e: messages.error(request, _(six.text_type(e))) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) try: check_duplicate_usernames(self.user_specs) except UserUploadError as e: messages.error(request, _(six.text_type(e))) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) task_ref = expose_cached_download(payload=None, expiry=1*60*60, file_extension=None) task = bulk_upload_async.delay( self.domain, self.user_specs, list(self.group_specs), ) task_ref.set_task(task) return HttpResponseRedirect( reverse( UserUploadStatusView.urlname, args=[self.domain, task_ref.download_id] ) )
def test_get_workbook_bad_file(self): bad_filepath = self.get_path('not_excel_file', 'xlsx') with self.assertRaises(WorkbookJSONError): get_workbook(bad_filepath)
def test_parser(self, location_type_mock, _): location_type_mock.by_domain.return_value = self.location_types location_type_mock.select_related.return_value.filter.return_value = self.location_types with tempfile.TemporaryFile() as file: export_raw(self.headers, self.rows, file, format=Format.XLS_2007) file.seek(0) workbook = get_workbook(file) parser = Parser(self.domain, workbook) errors = parser.parse() self.assertEqual(len(parser.valid_transitions['awc']), 2) awc_transitions = [attr.asdict(t) for t in parser.valid_transitions['awc']] self.assertEqual( awc_transitions, [ { 'domain': self.domain, 'location_type_code': 'awc', 'operation': 'Move', 'old_site_codes': ['112'], 'new_site_codes': ['131'], 'new_location_details': { '131': {'name': 'AWC 3 [131]', 'parent_site_code': '13', 'lgd_code': 'AWC-131', 'sub_district_name': None}}, 'user_transitions': {'username2': 'username3'} }, { 'domain': self.domain, 'location_type_code': 'awc', 'operation': 'Move', 'old_site_codes': ['115'], 'new_site_codes': ['133'], 'new_location_details': { '133': {'name': 'AWC 8 [133]', 'parent_site_code': '12', 'lgd_code': 'AWC-133', 'sub_district_name': None}}, 'user_transitions': {'username6': 'username7'} } ] ) self.assertEqual(len(parser.valid_transitions['supervisor']), 1) supervisor_transition = attr.asdict(parser.valid_transitions['supervisor'][0]) self.assertEqual( supervisor_transition, {'domain': self.domain, 'location_type_code': 'supervisor', 'operation': 'Move', 'old_site_codes': ['12'], 'new_site_codes': ['13'], 'new_location_details': { '13': { 'name': 'Supervisor 3 [13]', 'parent_site_code': '1', 'lgd_code': 'Sup-13', 'sub_district_name': None } }, 'user_transitions': {'username5': 'username6'}} ) self.assertEqual(len(parser.valid_transitions['state']), 1) state_transition = attr.asdict(parser.valid_transitions['state'][0]) self.assertEqual( state_transition, {'domain': self.domain, 'location_type_code': 'state', 'operation': 'Move', 'old_site_codes': ['2'], 'new_site_codes': ['3'], 'new_location_details': { '3': { 'name': 'State 3', 'parent_site_code': '', 'lgd_code': '', 'sub_district_name': None } }, 'user_transitions': {}} ) self.assertEqual(errors, [ "Invalid Operation Unknown", "Missing location code for operation Split. Got old: '11' and new: ''", "Got invalid location code 'invalid $ite #code' for operation Move", "Got invalid location code 'new-- $ite #code' for operation Move", "Got invalid parent location code '?--123--?' for new location " "'new-- $ite #code' for operation Move", "No change in location code for operation Extract. Got old: '111' and new: '111'", "New location 132 passed with different information", "Missing new location name for 134" ])
def test_dump(self, mock_active_site_codes_search, mock_inactive_site_codes_search, mock_case_count, mock_location_ids): mock_case_count.return_value = [] transitions = { 'awc': [ Transition(domain=self.domain, location_type_code='awc', operation=MOVE_OPERATION, old_site_codes=['111'], new_site_codes=['112']), Transition(domain=self.domain, location_type_code='awc', operation=MERGE_OPERATION, old_site_codes=['113', '114'], new_site_codes=['115']), Transition(domain=self.domain, location_type_code='awc', operation=SPLIT_OPERATION, old_site_codes=['116'], new_site_codes=['117', '118']), Transition(domain=self.domain, location_type_code='awc', operation=EXTRACT_OPERATION, old_site_codes=['119'], new_site_codes=['120']) ], 'supervisor': [ Transition(domain=self.domain, location_type_code='awc', operation=MOVE_OPERATION, old_site_codes=['12'], new_site_codes=['13']), ], 'state': {} } mock_location_ids.return_value = { '111': 'locationid111', '113': 'locationid113', '114': 'locationid114', '116': 'locationid116', '119': 'locationid119', '12': 'locationid12' } mock_active_site_codes_search.return_value.values_list.return_value = [ '112', '115', '117' ] mock_inactive_site_codes_search.return_value.values_list.return_value = [ '120' ] filestream = Dumper(self.domain).dump(transitions) workbook = get_workbook(filestream) awc_worksheet_rows = list(workbook.get_worksheet('awc')) supervisor_worksheet_rows = list(workbook.get_worksheet('supervisor')) self.assertEqual(supervisor_worksheet_rows, [{ OLD_LOCATION_CODE_COLUMN: '12', TRANSITION_COLUMN: MOVE_OPERATION, NEW_LOCATION_CODE_COLUMN: '13', MISSING_COLUMN: True, ARCHIVED_COLUMN: False, CASE_COUNT_COLUMN: 0 }]) self.assertEqual(awc_worksheet_rows, [{ OLD_LOCATION_CODE_COLUMN: '111', TRANSITION_COLUMN: MOVE_OPERATION, NEW_LOCATION_CODE_COLUMN: '112', MISSING_COLUMN: False, ARCHIVED_COLUMN: False, CASE_COUNT_COLUMN: 0 }, { OLD_LOCATION_CODE_COLUMN: '113', TRANSITION_COLUMN: MERGE_OPERATION, NEW_LOCATION_CODE_COLUMN: '115', MISSING_COLUMN: False, ARCHIVED_COLUMN: False, CASE_COUNT_COLUMN: 0 }, { OLD_LOCATION_CODE_COLUMN: '114', TRANSITION_COLUMN: MERGE_OPERATION, NEW_LOCATION_CODE_COLUMN: '115', MISSING_COLUMN: False, ARCHIVED_COLUMN: False, CASE_COUNT_COLUMN: 0 }, { OLD_LOCATION_CODE_COLUMN: '116', TRANSITION_COLUMN: SPLIT_OPERATION, NEW_LOCATION_CODE_COLUMN: '117', MISSING_COLUMN: False, ARCHIVED_COLUMN: False, CASE_COUNT_COLUMN: 0 }, { OLD_LOCATION_CODE_COLUMN: '116', TRANSITION_COLUMN: SPLIT_OPERATION, NEW_LOCATION_CODE_COLUMN: '118', MISSING_COLUMN: True, ARCHIVED_COLUMN: False, CASE_COUNT_COLUMN: 0 }, { OLD_LOCATION_CODE_COLUMN: '119', TRANSITION_COLUMN: EXTRACT_OPERATION, NEW_LOCATION_CODE_COLUMN: '120', MISSING_COLUMN: True, ARCHIVED_COLUMN: True, CASE_COUNT_COLUMN: 0 }])
def _get_workbook(self, uploaded_file, action_type): try: workbook = get_workbook(uploaded_file) except WorkbookJSONError as e: return None, [str(e)] return workbook, self._validate_workbook(workbook, action_type)
def _get_workbook(self, uploaded_file): try: workbook = get_workbook(uploaded_file) except WorkbookJSONError as e: return None, [str(e)] return workbook, self._workbook_is_valid(workbook)
def process_ui_translation_upload(app, trans_file): trans_dict = defaultdict(dict) # Use this to hard fail and not update any translations error_properties = [] # Use this to pass warnings without failing hard warnings = [] try: workbook = get_workbook(trans_file) except WorkbookJSONError as e: error_properties.append(str(e)) return trans_dict, error_properties, warnings commcare_version = get_commcare_version_from_workbook(workbook.wb) try: translations = workbook.get_worksheet(title='translations') except WorksheetNotFound: error_properties.append( _('Could not find sheet "translations" in uploaded file.')) return trans_dict, error_properties, warnings commcare_ui_strings = list( load_translations('en', version=2, commcare_version=commcare_version).keys()) default_trans = get_default_translations_for_download( app, commcare_version) lang_with_defaults = app.langs[get_index_for_defaults(app.langs)] # Find all of the ${0}-looking values in a translation. # Returns list like ["${0}", "${1}", ...] def _get_params(translation): if not translation: return [] return re.findall(r"\$.*?}", translation) # Create a string version of all parameters in a translation, suitable both for # comparison and for displaying in an error message. def _get_params_text(params): if not params: return "no parameters" # Reduce parameter list to the numbers alone, a set like {'0', '1', '2'} numbers = {re.sub(r'\D', '', p) for p in params} # Sort numbers, so that re-ordering parameters doesn't trigger error numbers = sorted(numbers) # Re-join numbers into a string for display return ", ".join(["${{{}}}".format(num) for num in numbers]) for row in translations: if row["property"] not in commcare_ui_strings: # Add a warning for unknown properties, but still add them to the translation dict warnings.append("Property '" + row["property"] + "' is not a known CommCare UI string, " "but we added it anyway.") default = default_trans.get(row["property"]) default_params_text = _get_params_text( _get_params(default)) if default else None for lang in app.langs: if row.get(lang): params = _get_params(row[lang]) params_text = _get_params_text(params) for param in params: if not re.match(r"\$\{[0-9]+}", param): error_properties.append( _(""" Could not understand '{param}' in {lang} value of '{prop}'. """).format(param=param, lang=lang, prop=row["property"])) if default_params_text and params_text != default_params_text: warnings.append( _(""" Property '{prop}' should contain {expected} but {lang} value contains {actual}. """).format(prop=row["property"], expected=default_params_text, lang=lang, actual=params_text)) if not (lang_with_defaults == lang and row[lang] == default_trans.get(row["property"], "")): trans_dict[lang].update({row["property"]: row[lang]}) return trans_dict, error_properties, warnings
def test_dump(self, get_location_mock, get_household_case_ids_mock, case_accessor_mock, child_cases_mock): location = Location(site_code='123', location_id='123654789') get_location_mock.return_value = location get_household_case_ids_mock.return_value = ['1', '2'] case_accessor_mock.return_value = CommCareCaseStub( '100', 'A House', {'hh_reg_date': '20/1/1988', 'hh_religion': 'Above All'}) child_cases_mock.return_value = [ CommCareCaseStub('101', 'A Person', {'age_at_reg': '4', 'sex': 'M'}), CommCareCaseStub('102', 'B Person', {'age_at_reg': '5', 'sex': 'F'}), ] transitions = [ Transition( domain=self.domain, location_type_code='awc', operation=MOVE_OPERATION, old_site_codes=['111'], new_site_codes=['112']), Transition( domain=self.domain, location_type_code='awc', operation=MERGE_OPERATION, old_site_codes=['113', '114'], new_site_codes=['115']), Transition( domain=self.domain, location_type_code='awc', operation=SPLIT_OPERATION, old_site_codes=['116'], new_site_codes=['117', '118']), Transition( domain=self.domain, location_type_code='awc', operation=EXTRACT_OPERATION, old_site_codes=['119'], new_site_codes=['120']) ] filestream = Households(self.domain).dump(transitions) get_location_mock.assert_has_calls([ call(domain='test', site_code='116'), call(domain='test', site_code='119') ]) get_household_case_ids_mock.assert_has_calls([ call(self.domain, location.location_id), call(self.domain, location.location_id) ]) case_accessor_mock.assert_has_calls([ call('1'), call('2') ]) child_cases_mock.assert_has_calls([ call(self.domain, '1', location.location_id, [PERSON_CASE_TYPE]), call(self.domain, '2', location.location_id, [PERSON_CASE_TYPE]) ]) workbook = get_workbook(filestream) self.assertEqual(len(workbook.worksheets), 2) expected_row = { AWC_NAME_COLUMN: '', AWC_CODE_COLUMN: '', 'Name of Household': 'A House', 'Date of Registration': '20/1/1988', 'Religion': 'Above All', 'Caste': '', 'APL/BPL': '', 'Number of Household Members': 2, HOUSEHOLD_MEMBER_DETAILS_COLUMN: 'A Person (4/M), B Person (5/F)', HOUSEHOLD_ID_COLUMN: '100' } worksheet1_rows = list(workbook.worksheets_by_title['116']) self.assertEqual( worksheet1_rows, [expected_row, expected_row] ) worksheet2_rows = list(workbook.worksheets_by_title['119']) self.assertEqual( worksheet2_rows, [expected_row, expected_row] )