@@ -1142,3 +1142,188 @@ def test_reprocess_kobo_resolves_household_refs(program, user: User, force_migra
11421142 assert household .flex_fields ["head_of_household_id" ] == head .pk
11431143 assert household .flex_fields ["primary_collector_id" ] == primary .pk
11441144 assert household .flex_fields ["alternate_collector_id" ] == alternate .pk
1145+
1146+
1147+ def test_apply_transformer_only_updates_record (program , user : User , force_migrated_records ) -> None :
1148+ from country_workspace .workspaces .admin .batch .reprocessing import _apply_transformer_only
1149+ from testutils .factories import CountryIndividualFactory , TransformerFactory
1150+
1151+ individual = CountryIndividualFactory (
1152+ household = None ,
1153+ batch__program = program ,
1154+ batch__country_office = program .country_office ,
1155+ flex_fields = {"gender" : "F" },
1156+ )
1157+ transformer = TransformerFactory (
1158+ office = program .country_office ,
1159+ value_transformations = "function t(d) { d['gender'] = 'FEMALE'; return d; }" ,
1160+ )
1161+
1162+ applied = _apply_transformer_only (individual , transformer )
1163+
1164+ assert applied is True
1165+ individual .refresh_from_db ()
1166+ assert individual .flex_fields ["gender" ] == "FEMALE"
1167+
1168+
1169+ def test_apply_transformer_only_returns_false_when_unchanged (program , user : User , force_migrated_records ) -> None :
1170+ from country_workspace .workspaces .admin .batch .reprocessing import _apply_transformer_only
1171+ from testutils .factories import CountryIndividualFactory , TransformerFactory
1172+
1173+ individual = CountryIndividualFactory (
1174+ household = None ,
1175+ batch__program = program ,
1176+ batch__country_office = program .country_office ,
1177+ flex_fields = {"gender" : "F" },
1178+ )
1179+ transformer = TransformerFactory (
1180+ office = program .country_office ,
1181+ value_transformations = "function t(d) { return d; }" ,
1182+ )
1183+
1184+ applied = _apply_transformer_only (individual , transformer )
1185+
1186+ assert applied is False
1187+
1188+
1189+ def test_apply_transformer_only_uses_empty_dict_for_non_dict_flex_fields (
1190+ program , user : User , force_migrated_records
1191+ ) -> None :
1192+ from country_workspace .workspaces .admin .batch .reprocessing import _apply_transformer_only
1193+ from testutils .factories import CountryIndividualFactory , TransformerFactory
1194+
1195+ individual = CountryIndividualFactory (
1196+ household = None ,
1197+ batch__program = program ,
1198+ batch__country_office = program .country_office ,
1199+ flex_fields = "unexpected" ,
1200+ )
1201+ transformer = TransformerFactory (
1202+ office = program .country_office ,
1203+ value_transformations = "function t(d) { d['created'] = true; return d; }" ,
1204+ )
1205+
1206+ applied = _apply_transformer_only (individual , transformer )
1207+
1208+ assert applied is True
1209+ individual .refresh_from_db ()
1210+ assert individual .flex_fields ["created" ] is True
1211+
1212+
1213+ def test_apply_batch_transformers_requires_transformer_ids (program , user : User ) -> None :
1214+ from country_workspace .workspaces .admin .batch .reprocessing import apply_batch_transformers
1215+ from testutils .factories import AsyncJobFactory , CountryBatchFactory
1216+
1217+ batch = CountryBatchFactory (program = program , country_office = program .country_office )
1218+ job = AsyncJobFactory (program = program , batch = batch , owner = user , config = {"batch_id" : batch .pk })
1219+
1220+ with pytest .raises (ValueError , match = "At least one transformer id is required" ):
1221+ apply_batch_transformers (job )
1222+
1223+
1224+ def test_apply_batch_transformers_applies_individual_only (program , user : User , force_migrated_records ) -> None :
1225+ from country_workspace .workspaces .admin .batch .reprocessing import apply_batch_transformers
1226+ from testutils .factories import AsyncJobFactory , CountryIndividualFactory , TransformerFactory
1227+
1228+ individual = CountryIndividualFactory (
1229+ household = None ,
1230+ batch__program = program ,
1231+ batch__country_office = program .country_office ,
1232+ flex_fields = {"status" : "1" },
1233+ )
1234+ batch = individual .batch
1235+ transformer = TransformerFactory (
1236+ office = program .country_office ,
1237+ value_transformations = "function t(d) { d['status'] = 'ACTIVE'; return d; }" ,
1238+ )
1239+ job = AsyncJobFactory (
1240+ program = program ,
1241+ batch = batch ,
1242+ owner = user ,
1243+ config = {"batch_id" : batch .pk , "individual_transformer_id" : transformer .pk },
1244+ )
1245+
1246+ with patch ("country_workspace.workspaces.admin.batch.reprocessing.sync_collector_links" ) as mock_sync :
1247+ result = apply_batch_transformers (job )
1248+
1249+ individual .refresh_from_db ()
1250+ assert individual .flex_fields ["status" ] == "ACTIVE"
1251+ assert result ["batch_id" ] == batch .pk
1252+ assert result ["transformed_individuals" ] == 1
1253+ mock_sync .assert_called_once ()
1254+
1255+
1256+ def test_apply_batch_transformers_applies_household_only_master_detail (
1257+ program , user : User , force_migrated_records
1258+ ) -> None :
1259+ from country_workspace .workspaces .admin .batch .reprocessing import apply_batch_transformers
1260+ from testutils .factories import AsyncJobFactory , CountryHouseholdFactory , TransformerFactory
1261+ from testutils .factories .program import BeneficiaryGroupFactory
1262+
1263+ bg = BeneficiaryGroupFactory (master_detail = True )
1264+ program .beneficiary_group = bg
1265+ program .save ()
1266+
1267+ household = CountryHouseholdFactory (
1268+ batch__program = program ,
1269+ batch__country_office = program .country_office ,
1270+ flex_fields = {"category" : "A" },
1271+ )
1272+ batch = household .batch
1273+ transformer = TransformerFactory (
1274+ office = program .country_office ,
1275+ value_transformations = "function t(d) { d['category'] = 'B'; return d; }" ,
1276+ )
1277+ job = AsyncJobFactory (
1278+ program = program ,
1279+ batch = batch ,
1280+ owner = user ,
1281+ config = {"batch_id" : batch .pk , "household_transformer_id" : transformer .pk },
1282+ )
1283+
1284+ with patch ("country_workspace.workspaces.admin.batch.reprocessing._sync_household_refs" ) as mock_sync :
1285+ result = apply_batch_transformers (job )
1286+
1287+ household .refresh_from_db ()
1288+ assert household .flex_fields ["category" ] == "B"
1289+ assert result ["transformed_households" ] == 1
1290+ assert result ["transformed_individuals" ] == 0
1291+ mock_sync .assert_called_once_with (batch )
1292+
1293+
1294+ def test_apply_batch_transformers_skips_households_for_non_master_detail (
1295+ program , user : User , force_migrated_records
1296+ ) -> None :
1297+ from country_workspace .workspaces .admin .batch .reprocessing import apply_batch_transformers
1298+ from testutils .factories import AsyncJobFactory , CountryHouseholdFactory , TransformerFactory
1299+ from testutils .factories .program import BeneficiaryGroupFactory
1300+
1301+ bg = BeneficiaryGroupFactory (master_detail = False )
1302+ program .beneficiary_group = bg
1303+ program .save ()
1304+
1305+ household = CountryHouseholdFactory (
1306+ batch__program = program ,
1307+ batch__country_office = program .country_office ,
1308+ flex_fields = {"category" : "A" },
1309+ )
1310+ batch = household .batch
1311+ transformer = TransformerFactory (
1312+ office = program .country_office ,
1313+ value_transformations = "function t(d) { d['category'] = 'B'; return d; }" ,
1314+ )
1315+ job = AsyncJobFactory (
1316+ program = program ,
1317+ batch = batch ,
1318+ owner = user ,
1319+ config = {"batch_id" : batch .pk , "household_transformer_id" : transformer .pk },
1320+ )
1321+
1322+ with patch ("country_workspace.workspaces.admin.batch.reprocessing._sync_household_refs" ) as mock_sync :
1323+ result = apply_batch_transformers (job )
1324+
1325+ household .refresh_from_db ()
1326+ assert household .flex_fields ["category" ] == "A"
1327+ assert "transformed_households" not in result
1328+ assert result ["transformed_individuals" ] == 0
1329+ mock_sync .assert_not_called ()
0 commit comments