Skip to content

Commit 1b3d057

Browse files
Merge pull request #703 from akvo/feature/702-import-show-recalculate-segment-number_of_farmers-based-on-threshold
Feature/702 import show recalculate segment number of farmers based on threshold
2 parents 0c3f2a5 + a5bd727 commit 1b3d057

File tree

6 files changed

+659
-437
lines changed

6 files changed

+659
-437
lines changed

backend/models/case_import.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,6 @@ class GenerateSegmentValuesRequest(BaseModel):
4444
segments: List[SegmentDefinition]
4545

4646

47-
# class CategoricalCondition(BaseModel):
48-
# operator: Literal["is"]
49-
# value: str
50-
51-
52-
# class NumericalCondition(BaseModel):
53-
# operator: Literal["<=", ">", "between"]
54-
# value: Union[float, List[float]]
55-
56-
5747
class SegmentationSegment(BaseModel):
5848
index: int
5949
name: str
@@ -69,6 +59,21 @@ class SegmentationPreviewResponse(BaseModel):
6959
segments: List[SegmentationSegment]
7060

7161

62+
class SegmentValueInput(BaseModel):
63+
index: int = Field(..., ge=1)
64+
value: float | str
65+
66+
67+
class SegmentationRecalculateRequest(BaseModel):
68+
import_id: str
69+
segmentation_variable: str
70+
variable_type: Literal["numerical", "categorical"]
71+
segments: List[SegmentValueInput]
72+
73+
74+
# TODO ::
75+
# Do we need to save the segmentation_variable, variable_type,
76+
# number of segments (segment configuration) into the DB?
7277
class CaseImport(Base):
7378
__tablename__ = "case_import"
7479

backend/routes/case_import.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
generate_categorical_segments,
2828
generate_numerical_segments,
2929
validate_ready_for_upload,
30+
recalculate_numerical_segments,
3031
)
3132
from utils.case_import_storage import save_import_file, load_import_file
3233
from utils.case_import_process_confirmed_segmentation import (
@@ -37,6 +38,7 @@
3738
SegmentationPreviewRequest,
3839
SegmentationPreviewResponse,
3940
GenerateSegmentValuesRequest,
41+
SegmentationRecalculateRequest,
4042
)
4143

4244
security = HTTPBearer()
@@ -226,3 +228,73 @@ def download_upload_template():
226228
filename=TEMPLATE_NAME,
227229
media_type="application/vnd.ms-excel.sheet.macroEnabled.12",
228230
)
231+
232+
233+
@case_import_route.post(
234+
"/case-import/recalculate-segmentation",
235+
summary="Recalculate segmentation after user edits segment values",
236+
name="case_import:recalculate_segmentation",
237+
tags=ROUTE_TAG_NAME,
238+
)
239+
def recalculate_segmentation(
240+
req: Request,
241+
payload: SegmentationRecalculateRequest,
242+
session: Session = Depends(get_session),
243+
credentials: credentials = Depends(security),
244+
):
245+
verify_case_creator(
246+
session=session,
247+
authenticated=req.state.authenticated,
248+
)
249+
250+
case_import = get_case_import(
251+
session=session,
252+
import_id=payload.import_id,
253+
)
254+
255+
content = load_import_file(case_import.file_path)
256+
df = load_data_dataframe_from_bytes(content)
257+
258+
variable = payload.segmentation_variable.lower()
259+
var_type = payload.variable_type.lower()
260+
261+
if variable not in df.columns:
262+
raise HTTPException(
263+
status_code=400,
264+
detail="Segmentation variable not found in data sheet",
265+
)
266+
267+
# -------- CATEGORICAL --------
268+
if var_type == "categorical":
269+
# No recalculation needed; categories are fixed
270+
segments = generate_categorical_segments(
271+
df=df,
272+
column=variable,
273+
)
274+
275+
# -------- NUMERICAL --------
276+
elif var_type == "numerical":
277+
if not payload.segments:
278+
raise HTTPException(
279+
status_code=400,
280+
detail="Segments are required for numerical recalculation",
281+
)
282+
283+
segments = recalculate_numerical_segments(
284+
df=df,
285+
column=variable,
286+
segments=[seg.dict() for seg in payload.segments],
287+
)
288+
289+
else:
290+
raise HTTPException(
291+
status_code=400,
292+
detail="Invalid variable type",
293+
)
294+
295+
return {
296+
"import_id": payload.import_id,
297+
"segmentation_variable": variable,
298+
"variable_type": var_type,
299+
"segments": segments,
300+
}

0 commit comments

Comments
 (0)