Skip to content

Commit 8eea531

Browse files
Increase coverage
1 parent 701a616 commit 8eea531

File tree

4 files changed

+331
-115
lines changed

4 files changed

+331
-115
lines changed

src/country_workspace/config/settings.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -195,22 +195,9 @@
195195
"": {
196196
"handlers": ["console"],
197197
"level": env("LOGGING_LEVEL"),
198-
"propagate": True,
199198
},
200199
"celery": {
201-
"handlers": ["console"],
202-
"level": "ERROR",
203-
"propagate": False,
204-
},
205-
"faker": {
206-
"handlers": ["console"],
207-
"level": "ERROR",
208-
"propagate": False,
209-
},
210-
"factory": {
211-
"handlers": ["console"],
212200
"level": "ERROR",
213-
"propagate": False,
214201
},
215202
},
216203
}
Lines changed: 84 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections.abc import Callable
12
from typing import Any, Final, Mapping
23

34
from django.db.models import Model
@@ -21,6 +22,22 @@
2122
MODELS: Final[tuple[type[Model], ...]] = (Office, Program, BeneficiaryGroup)
2223
"""List of models to synchronize."""
2324

25+
OFFICE_FIELDS = "name", "slug", "code", "long_name", "active"
26+
BENEFICIARY_GROUP_FIELDS = (
27+
"name",
28+
"group_label",
29+
"group_label_plural",
30+
"member_label",
31+
"member_label_plural",
32+
"master_detail",
33+
)
34+
Program_FIELDS = "name", "code", "status", "sector", "country_office", "beneficiary_group"
35+
36+
HOPE_ID = "hope_id"
37+
BUSINESS_AREAS = "business_areas"
38+
BENEFICIARY_GROUPS = "beneficiary-groups"
39+
PROGRAMS = "programs"
40+
2441

2542
def get_default_checkers() -> Mapping[str, DataChecker]:
2643
return {
@@ -33,15 +50,26 @@ def get_default_checkers() -> Mapping[str, DataChecker]:
3350
}
3451

3552

53+
def get_field_extractor(fields: tuple[str, ...]) -> Callable[[dict[str, Any]], dict[str, Any]]:
54+
def field_extractor(record: dict[str, Any]) -> dict[str, Any]:
55+
return {field: record.get(field) for field in fields}
56+
57+
return field_extractor
58+
59+
60+
def should_process_office(record: dict[str, Any]) -> bool:
61+
return bool(record.get("active"))
62+
63+
3664
def sync_offices(delta_sync: bool = False) -> None:
3765
"""Fetch and process Office records from the remote API, deactivating those not present in the source."""
3866
sync_entity(
39-
SyncConfig(
67+
SyncConfig[Office](
4068
model=Office,
41-
reference_id="hope_id",
42-
endpoint=build_endpoint("business_areas", Office, ParamDateName.UPDATED, delta_sync),
43-
prepare_defaults=lambda r: {f: r.get(f) for f in ("name", "slug", "code", "long_name", "active")},
44-
should_process=lambda r: r.get("active"),
69+
reference_id=HOPE_ID,
70+
endpoint=build_endpoint(BUSINESS_AREAS, Office, ParamDateName.UPDATED, delta_sync),
71+
prepare_defaults=get_field_extractor(OFFICE_FIELDS),
72+
should_process=should_process_office,
4573
delta_sync=delta_sync,
4674
)
4775
)
@@ -50,75 +78,72 @@ def sync_offices(delta_sync: bool = False) -> None:
5078
def sync_beneficiary_groups(delta_sync: bool = False) -> None:
5179
"""Fetch and process BeneficiaryGroup records from the remote API."""
5280
sync_entity(
53-
SyncConfig(
81+
SyncConfig[BeneficiaryGroup](
5482
model=BeneficiaryGroup,
55-
reference_id="hope_id",
56-
endpoint=EndpointConfig(path="beneficiary-groups"),
57-
prepare_defaults=lambda r: {
58-
f: r.get(f)
59-
for f in (
60-
"name",
61-
"group_label",
62-
"group_label_plural",
63-
"member_label",
64-
"member_label_plural",
65-
"master_detail",
66-
)
67-
},
83+
reference_id=HOPE_ID,
84+
endpoint=EndpointConfig(path=BENEFICIARY_GROUPS),
85+
prepare_defaults=get_field_extractor(BENEFICIARY_GROUP_FIELDS),
6886
delta_sync=delta_sync,
6987
)
7088
)
7189

7290

91+
def get_should_process_program(programs_limit_to_office: Office | None) -> Callable[[dict[str, Any]], bool]:
92+
def should_process_program(record: dict[str, Any]) -> bool:
93+
return record.get("status") in [Program.ACTIVE, Program.DRAFT] and (
94+
not programs_limit_to_office or record["business_area_code"] == programs_limit_to_office.code
95+
)
96+
97+
return should_process_program
98+
99+
100+
def prepare_program_defaults(record: dict[str, Any]) -> dict[str, Any] | None:
101+
try:
102+
office = Office.objects.get(code=record["business_area_code"])
103+
except Office.DoesNotExist as e:
104+
raise SkipRecordError("Office not found") from e
105+
106+
try:
107+
bg = BeneficiaryGroup.objects.get(hope_id=record["beneficiary_group"])
108+
except BeneficiaryGroup.DoesNotExist as e:
109+
raise SkipRecordError("Beneficiary group not found") from e
110+
111+
return {
112+
"name": record["name"],
113+
"code": record["programme_code"],
114+
"status": record["status"],
115+
"sector": record["sector"],
116+
"country_office": office,
117+
"beneficiary_group": bg,
118+
}
119+
120+
121+
def post_process_program(program: Program, created: bool) -> None:
122+
default_checkers = get_default_checkers()
123+
if created and default_checkers:
124+
program.household_checker = default_checkers.get("hh")
125+
program.individual_checker = (
126+
default_checkers.get("ind") if program.beneficiary_group.master_detail else default_checkers.get("ppl")
127+
)
128+
if program.household_checker or program.individual_checker:
129+
program.save(update_fields=("household_checker", "individual_checker"))
130+
131+
73132
def sync_programs(delta_sync: bool = False, programs_limit_to_office: Office | None = None) -> None:
74133
"""Synchronize and process Program records from the remote API, applying filters and post-processing.
75134
76135
Notes:
77136
Calls sync_beneficiary_groups to ensure dependencies are synchronized.
78137
79138
"""
80-
81-
def _should_process(record: dict[str, Any]) -> bool:
82-
return record.get("status") in [Program.ACTIVE, Program.DRAFT] and (
83-
not programs_limit_to_office or record["business_area_code"] == programs_limit_to_office.code
84-
)
85-
86-
def _prepare_defaults(record: dict[str, Any]) -> dict[str, Any] | None:
87-
try:
88-
office = Office.objects.get(code=record["business_area_code"])
89-
except Office.DoesNotExist as e:
90-
raise SkipRecordError("Office not found") from e
91-
try:
92-
bg = BeneficiaryGroup.objects.get(hope_id=record["beneficiary_group"])
93-
except BeneficiaryGroup.DoesNotExist as e:
94-
raise SkipRecordError("Beneficiary group not found") from e
95-
return {
96-
"name": record["name"],
97-
"code": record["programme_code"],
98-
"status": record["status"],
99-
"sector": record["sector"],
100-
"country_office": office,
101-
"beneficiary_group": bg,
102-
}
103-
104-
def _post_process(program: Program, created: bool) -> None:
105-
default_checkers = get_default_checkers()
106-
if created and default_checkers:
107-
program.household_checker = default_checkers.get("hh")
108-
program.individual_checker = (
109-
default_checkers.get("ind") if program.beneficiary_group.master_detail else default_checkers.get("ppl")
110-
)
111-
if program.household_checker or program.individual_checker:
112-
program.save(update_fields=("household_checker", "individual_checker"))
113-
114139
sync_entity(
115-
SyncConfig(
140+
SyncConfig[Program](
116141
model=Program,
117-
reference_id="hope_id",
118-
endpoint=build_endpoint("programs", Program, ParamDateName.UPDATED, delta_sync),
119-
prepare_defaults=_prepare_defaults,
120-
should_process=_should_process,
121-
post_process=_post_process,
142+
reference_id=HOPE_ID,
143+
endpoint=build_endpoint(PROGRAMS, Program, ParamDateName.UPDATED, delta_sync),
144+
prepare_defaults=prepare_program_defaults,
145+
should_process=get_should_process_program(programs_limit_to_office),
146+
post_process=post_process_program,
122147
delta_sync=delta_sync,
123148
)
124149
)

0 commit comments

Comments
 (0)