From 087dd7e3fa83d7bcf56571a72d54704f67238470 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 09:47:50 +0800 Subject: [PATCH 1/8] [#185] Remove reporting period field --- .../src/pages/cases/components/CaseProfile.js | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/frontend/src/pages/cases/components/CaseProfile.js b/frontend/src/pages/cases/components/CaseProfile.js index 85b00e7a..4437fe66 100644 --- a/frontend/src/pages/cases/components/CaseProfile.js +++ b/frontend/src/pages/cases/components/CaseProfile.js @@ -217,23 +217,6 @@ const CaseForm = ({ - - - @@ -456,9 +439,9 @@ const CaseProfile = ({ currency: values.currency, area_size_unit: values.area_size_unit, volume_measurement_unit: values.volume_measurement_unit, - reporting_period: values.reporting_period, multiple_commodities: secondary || tertiary, - // need to handle below value correctly + reporting_period: "per-year", + // TODO:: need to handle below value correctly cost_of_production_unit: "cost_of_production_unit", segmentation: true, living_income_study: null, From 0cb36bfc9c8d970f5f94d63ca877697ec59faaf1 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 09:49:18 +0800 Subject: [PATCH 2/8] [#185] Fix yarn lint --- frontend/src/pages/cases/components/CaseProfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/pages/cases/components/CaseProfile.js b/frontend/src/pages/cases/components/CaseProfile.js index 4437fe66..5bb7bba4 100644 --- a/frontend/src/pages/cases/components/CaseProfile.js +++ b/frontend/src/pages/cases/components/CaseProfile.js @@ -27,7 +27,6 @@ import { commodityOptions, countryOptions, currencyOptions, - reportingPeriod, selectProps, yesNoOptions, DebounceSelect, From fcd4e30d7a542742ab934b83a4a56edbaf607842 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 09:49:33 +0800 Subject: [PATCH 3/8] [#185] Handle "per-year" as default reporting period --- backend/db/crud_case.py | 8 ++++++-- backend/models/case.py | 16 +++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/backend/db/crud_case.py b/backend/db/crud_case.py index dba3c365..3990a9b7 100644 --- a/backend/db/crud_case.py +++ b/backend/db/crud_case.py @@ -21,6 +21,8 @@ class PaginatedCaseData(TypedDict): def add_case(session: Session, payload: CaseBase, user: User) -> CaseDict: + reporting_period = payload.reporting_period + reporting_period = reporting_period if reporting_period else "per-year" current_datetime = datetime.now() case = Case( name=payload.name, @@ -33,7 +35,7 @@ def add_case(session: Session, payload: CaseBase, user: User) -> CaseDict: area_size_unit=payload.area_size_unit, volume_measurement_unit=payload.volume_measurement_unit, cost_of_production_unit=payload.cost_of_production_unit, - reporting_period=payload.reporting_period, + reporting_period=reporting_period, segmentation=1 if payload.segmentation else 0, living_income_study=payload.living_income_study, multiple_commodities=1 if payload.multiple_commodities else 0, @@ -140,12 +142,14 @@ def update_case(session: Session, id: int, payload: CaseBase) -> CaseDict: case.area_size_unit = payload.area_size_unit case.volume_measurement_unit = payload.volume_measurement_unit case.cost_of_production_unit = payload.cost_of_production_unit - case.reporting_period = payload.reporting_period case.segmentation = 1 if payload.segmentation else 0 case.living_income_study = payload.living_income_study case.multiple_commodities = 1 if payload.multiple_commodities else 0 case.logo = payload.logo case.private = 1 if payload.private else 0 + # reporting period + if payload.reporting_period: + case.reporting_period = payload.reporting_period # handle tag if payload.tags: prev_tags = ( diff --git a/backend/models/case.py b/backend/models/case.py index 9049eaa3..59774dc0 100644 --- a/backend/models/case.py +++ b/backend/models/case.py @@ -113,7 +113,8 @@ class Case(Base): reporting_period = Column(String, nullable=False) segmentation = Column(SmallInteger, nullable=False, default=0) living_income_study = Column( - Enum(LivingIncomeStudyEnum, name="case_living_income_study"), nullable=True + Enum(LivingIncomeStudyEnum, name="case_living_income_study"), + nullable=True, ) multiple_commodities = Column(SmallInteger, nullable=False, default=0) private = Column(SmallInteger, nullable=False, default=0) @@ -121,7 +122,10 @@ class Case(Base): created_by = Column(Integer, ForeignKey("user.id")) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column( - DateTime, nullable=False, server_default=func.now(), onupdate=func.now() + DateTime, + nullable=False, + server_default=func.now(), + onupdate=func.now(), ) case_commodities = relationship( @@ -166,8 +170,8 @@ def __init__( area_size_unit: str, volume_measurement_unit: str, cost_of_production_unit: str, - reporting_period: str, segmentation: int, + reporting_period: Optional[str], living_income_study: Optional[str], description: Optional[str], multiple_commodities: int, @@ -266,7 +270,9 @@ def to_case_detail(self) -> CaseDetailDict: "created_by": self.created_by_user.email, "created_at": self.created_at.strftime("%Y-%m-%d %H:%M:%S"), "updated_at": self.updated_at.strftime("%Y-%m-%d %H:%M:%S"), - "segments": [ps.serialize_with_answers for ps in self.case_segments], + "segments": [ + ps.serialize_with_answers for ps in self.case_segments + ], "case_commodities": [pc.simplify for pc in self.case_commodities], "private": self.private, "tags": [ct.tag for ct in self.case_tags], @@ -299,9 +305,9 @@ class CaseBase(BaseModel): area_size_unit: str volume_measurement_unit: str cost_of_production_unit: str - reporting_period: str segmentation: bool multiple_commodities: bool + reporting_period: Optional[str] = None living_income_study: Optional[LivingIncomeStudyEnum] = None logo: Optional[str] = None private: Optional[bool] = False From 20ab33708fa6ad205dab34164a922e12de9396bb Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 09:59:30 +0800 Subject: [PATCH 4/8] [#176] Remove unused/commented code (old layout) --- .../src/pages/cases/components/Scenario.js | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/frontend/src/pages/cases/components/Scenario.js b/frontend/src/pages/cases/components/Scenario.js index 496ad2bd..0319a985 100644 --- a/frontend/src/pages/cases/components/Scenario.js +++ b/frontend/src/pages/cases/components/Scenario.js @@ -1110,59 +1110,6 @@ const Scenario = ({ - - {/* setActiveTab(key)} - > - - - {dashboardData.map((segment) => ( - - s.segmentId === segment.id - )} - scenarioItem={scenarioItem} - setScenarioData={setScenarioData} - currencyUnitName={currencyUnitName} - enableEditCase={enableEditCase} - /> - - ))} - - - - - - */} ); }; From 5586a58e454b66e7c8a59aa883f9c01b446624c7 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 10:54:38 +0800 Subject: [PATCH 5/8] [#176][#187] Refine scenario outcome value --- .../src/pages/cases/components/Scenario.js | 35 +++++++++++++------ frontend/src/pages/cases/components/index.js | 1 + 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/frontend/src/pages/cases/components/Scenario.js b/frontend/src/pages/cases/components/Scenario.js index 0319a985..e667e91d 100644 --- a/frontend/src/pages/cases/components/Scenario.js +++ b/frontend/src/pages/cases/components/Scenario.js @@ -95,6 +95,10 @@ const Question = ({

Total Income from {commodity_name} ({currency})

+ ) : question_type === "diversified" ? ( +

+ {commodity_name} ({currency}) +

) : (

{text} ({unitName}) @@ -115,7 +119,6 @@ const Question = ({ ? "none" : "", }} - // disabled={disableTotalIncomeFocusCommodityField} > + {/* Render questions */} {!parent && commodity_type === "focus" ? childrens.map((child) => ( { - return commodityQuestions.find((cq) => cq.commodity_type === "focus"); - }, [commodityQuestions]); + // const focusCommodity = useMemo(() => { + // return commodityQuestions.find((cq) => cq.commodity_type === "focus"); + // }, [commodityQuestions]); + console.log(commodityQuestions); const outcomeDriverQuestions = useMemo(() => { - const focus = focusCommodity?.questions[0]?.childrens?.map((x) => ({ - ...x, - case_commodity: focusCommodity.case_commodity, - })); + const commodities = commodityQuestions + .filter( + (cq) => + cq.commodity_type === "focus" && cq.commodity_type !== "diversified" + ) + .flatMap((cq) => { + const questions = cq.questions.find((q) => !q.parent); + return questions.childrens?.map((x) => ({ + ...x, + case_commodity: cq.case_commodity, + commodity_name: cq.commodity_name, + })); + }); let diversified = commodityQuestions.find( (cq) => cq.commodity_type === "diversified" ); diversified = diversified?.questions?.map((x) => ({ ...x, case_commodity: diversified.case_commodity, + commodity_name: diversified.commodity_name, })); - return [...focus, ...diversified].map((q) => ({ + return [...commodities, ...diversified].map((q) => ({ questionId: q.id, text: q.text, questionType: q.question_type, caseCommodityId: q.case_commodity, })); - }, [commodityQuestions, focusCommodity]); + }, [commodityQuestions]); const combineScenarioDataWithDashboardData = useMemo(() => { const scenarioKeys = []; diff --git a/frontend/src/pages/cases/components/index.js b/frontend/src/pages/cases/components/index.js index 71cd13f2..83494d81 100644 --- a/frontend/src/pages/cases/components/index.js +++ b/frontend/src/pages/cases/components/index.js @@ -175,6 +175,7 @@ export const customFormula = { revenue_focus_commodity: "#2 * #3 * #4", focus_commodity_cost_of_production: "( ( #5 * #2 ) + ( #26 * #3 * #2 ) ) * -1", + total_diversified_income: "#35 + #36 + #37", }; /** From ce3efafcefca1ff6565c83d114466629e7895bf2 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 10:57:16 +0800 Subject: [PATCH 6/8] [#181][#187] Clean up commented code --- .../src/pages/cases/components/Scenario.js | 56 ------------------- 1 file changed, 56 deletions(-) diff --git a/frontend/src/pages/cases/components/Scenario.js b/frontend/src/pages/cases/components/Scenario.js index e667e91d..f667c855 100644 --- a/frontend/src/pages/cases/components/Scenario.js +++ b/frontend/src/pages/cases/components/Scenario.js @@ -517,11 +517,6 @@ const Scenario = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [dashboardData]); - // const focusCommodity = useMemo(() => { - // return commodityQuestions.find((cq) => cq.commodity_type === "focus"); - // }, [commodityQuestions]); - console.log(commodityQuestions); - const outcomeDriverQuestions = useMemo(() => { const commodities = commodityQuestions .filter( @@ -597,21 +592,13 @@ const Scenario = ({ ]); const chartData = useMemo(() => { - // const totalFocusIncomeQuestion = focusCommodity.questions[0]; const data = combineScenarioDataWithDashboardData.map((d) => { const incomeTarget = d.currentSegmentValue.target; const currentTotalIncome = d.currentSegmentValue.total_current_income; - // const currentFocusIncome = - // d.currentSegmentValue.total_current_focus_income; const feasibleFocusIncome = d.currentSegmentValue.total_feasible_focus_income; const newTotalIncome = d?.newTotalIncome || feasibleFocusIncome; - // let newFocusCommodityIncome = - // d.allNewValues?.[ - // `absolute-${focusCommodity.case_commodity}-${totalFocusIncomeQuestion.id}` - // ] || feasibleFocusIncome; - // newFocusCommodityIncome = parseFloat(newFocusCommodityIncome); const additionalValue = newTotalIncome - currentTotalIncome; let gapValue = incomeTarget - newTotalIncome; @@ -649,49 +636,6 @@ const Scenario = ({ }; }); return data; - // const data = dashboardData.map((segment) => { - // const scenarioIncome = - // scenarioValues.find((s) => s.segmentId === segment.id)?.value || 0; - // const increaseIncome = scenarioIncome - // ? scenarioIncome - segment.total_current_income - // : scenarioIncome; - // const gapValue = scenarioIncome - // ? scenarioIncome >= segment.target - // ? 0 - // : segment.target - scenarioIncome - // : segment.target - segment.total_current_income; - // return { - // name: segment.name, - // title: segment.name, - // stack: [ - // { - // name: "Current Income", - // title: "Current Income", - // order: 1, - // total: segment.total_current_income, - // value: segment.total_current_income, - // color: "#00625F", - // }, - // { - // name: "Income Increase", - // title: "Income Increase", - // order: 2, - // total: increaseIncome, - // value: increaseIncome, - // color: "#47D985", - // }, - // { - // name: "Gap", - // title: "Gap", - // order: 3, - // total: gapValue > 0 ? gapValue : 0, - // value: gapValue > 0 ? gapValue : 0, - // color: "#F1C5B2", - // }, - // ], - // }; - // }); - // return data; }, [combineScenarioDataWithDashboardData]); const targetChartData = useMemo(() => { From b74e20a8b1f8d084a4a4f598511039eb63109b44 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 12:17:27 +0800 Subject: [PATCH 7/8] [#187] Group diversified income field & customized grouped value calculation --- .../components/DashboardScenarioModeling.js | 33 ++++++++- .../src/pages/cases/components/Scenario.js | 74 ++++++++++++++++--- frontend/src/pages/cases/components/index.js | 1 - 3 files changed, 93 insertions(+), 15 deletions(-) diff --git a/frontend/src/pages/cases/components/DashboardScenarioModeling.js b/frontend/src/pages/cases/components/DashboardScenarioModeling.js index 716ec4a6..d9ee78fe 100644 --- a/frontend/src/pages/cases/components/DashboardScenarioModeling.js +++ b/frontend/src/pages/cases/components/DashboardScenarioModeling.js @@ -44,10 +44,35 @@ const DashboardScenarioModeling = ({ }, [enableEditCase, scenarioData]); const commodityQuestions = useMemo(() => { - return commodityList.map((c) => ({ - ...c, - ...questionGroups.find((qg) => qg.commodity_id === c.commodity), - })); + return commodityList.map((c) => { + const findQG = questionGroups.find( + (qg) => qg.commodity_id === c.commodity + ); + if (c.commodity_type === "diversified") { + return { + ...c, + ...findQG, + questions: [ + { + ...findQG.questions[0], + id: "diversified", + default_value: findQG.questions + .map((x) => `#${x.id}`) + .join(" + "), + parent: null, + question_type: "diversified", + text: "Diversified Income", + description: "Custom question", + childrens: findQG.questions, + }, + ], + }; + } + return { + ...c, + ...findQG, + }; + }); }, [commodityList, questionGroups]); const renameScenario = (index, newName, newDescription) => { diff --git a/frontend/src/pages/cases/components/Scenario.js b/frontend/src/pages/cases/components/Scenario.js index f667c855..34d598e9 100644 --- a/frontend/src/pages/cases/components/Scenario.js +++ b/frontend/src/pages/cases/components/Scenario.js @@ -56,13 +56,34 @@ const Question = ({ }, [unit, commodity]); const answer = useMemo(() => { + if (id === "diversified") { + const answers = childrens + .map((c) => { + return segment.answers.find( + (s) => + s.question.id === c.id && + s.caseCommodityId === case_commodity && + s.name === "current" + ); + }) + .filter((x) => x); + return { + ...answers[0], + id: id, + question: childrens.map((c) => ({ + ...c, + value: answers.find((a) => a.question.id === c.id)?.value || 0, + })), + value: answers.reduce((res, cur) => res + cur.value, 0), + }; + } return segment.answers.find( (s) => s.question.id === id && s.caseCommodityId === case_commodity && s.name === "current" ); - }, [segment, id, case_commodity]); + }, [segment, id, case_commodity, childrens]); const currentIncrease = useMemo(() => { let value = 0; @@ -196,12 +217,39 @@ const ScenarioInput = ({ const onValuesChange = (changedValues, allValues) => { const objectId = Object.keys(changedValues)[0]; const [, case_commodity, id] = objectId.split("-"); - const segmentAnswer = segment.answers.find( - (s) => - s.questionId === parseInt(id) && - s.caseCommodityId === parseInt(case_commodity) && - s.name === "current" - ); + + let segmentAnswer = {}; + if (id === "diversified") { + const childrens = commodityQuestions + .find((cq) => cq.commodity_type === "diversified") + ?.questions?.find((q) => !q.parent)?.childrens; + const answers = childrens + ?.map((c) => { + return segment.answers.find( + (s) => + s.question.id === c.id && + s.caseCommodityId === parseInt(case_commodity) && + s.name === "current" + ); + }) + .filter((x) => x); + segmentAnswer = { + ...answers[0], + id: id, + question: childrens.map((c) => ({ + ...c, + value: answers.find((a) => a.question.id === c.id)?.value || 0, + })), + value: answers.reduce((res, cur) => res + cur.value, 0), + }; + } else { + segmentAnswer = segment.answers.find( + (s) => + s.questionId === parseInt(id) && + s.caseCommodityId === parseInt(case_commodity) && + s.name === "current" + ); + } const parentQuestion = segment.answers.find( (s) => @@ -258,14 +306,20 @@ const ScenarioInput = ({ ); const allNewValues = { ...allValues, ...newFieldsValue }; - const totalValues = allParentQuestions.reduce((acc, p) => { + let totalValues = allParentQuestions.reduce((acc, p) => { const questionId = `absolute-${p.caseCommodityId}-${p.question.id}`; - const value = allNewValues[questionId]; + const value = allNewValues?.[questionId] || 0; if (value) { acc += parseFloat(value); } return acc; }, 0); + // handle grouped diversified value + const newDiversified = + allNewValues?.[`absolute-${parseInt(case_commodity)}-diversified`]; + totalValues = + totalValues + (newDiversified ? parseFloat(newDiversified) : 0); + // const newScenarioValue = { segmentId: segment.id, @@ -318,7 +372,7 @@ const ScenarioInput = ({

Income Target

-

{`${segment.target} ${currencyUnitName}`}

+

{`${segment.target?.toFixed(2)} ${currencyUnitName}`}

diff --git a/frontend/src/pages/cases/components/index.js b/frontend/src/pages/cases/components/index.js index 83494d81..71cd13f2 100644 --- a/frontend/src/pages/cases/components/index.js +++ b/frontend/src/pages/cases/components/index.js @@ -175,7 +175,6 @@ export const customFormula = { revenue_focus_commodity: "#2 * #3 * #4", focus_commodity_cost_of_production: "( ( #5 * #2 ) + ( #26 * #3 * #2 ) ) * -1", - total_diversified_income: "#35 + #36 + #37", }; /** From 753c78acc237b2b6eb4874299004e533daef7b89 Mon Sep 17 00:00:00 2001 From: wayangalihpratama Date: Wed, 3 Jan 2024 13:16:09 +0800 Subject: [PATCH 8/8] [#187][#182] Fix scenario outcome driver changed value --- .../components/DashboardScenarioModeling.js | 1 + .../src/pages/cases/components/Scenario.js | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/cases/components/DashboardScenarioModeling.js b/frontend/src/pages/cases/components/DashboardScenarioModeling.js index d9ee78fe..86baa2e8 100644 --- a/frontend/src/pages/cases/components/DashboardScenarioModeling.js +++ b/frontend/src/pages/cases/components/DashboardScenarioModeling.js @@ -48,6 +48,7 @@ const DashboardScenarioModeling = ({ const findQG = questionGroups.find( (qg) => qg.commodity_id === c.commodity ); + // handle grouped diversified income question if (c.commodity_type === "diversified") { return { ...c, diff --git a/frontend/src/pages/cases/components/Scenario.js b/frontend/src/pages/cases/components/Scenario.js index 34d598e9..d98aa799 100644 --- a/frontend/src/pages/cases/components/Scenario.js +++ b/frontend/src/pages/cases/components/Scenario.js @@ -56,6 +56,7 @@ const Question = ({ }, [unit, commodity]); const answer = useMemo(() => { + // handle grouped diversified value if (id === "diversified") { const answers = childrens .map((c) => { @@ -219,6 +220,7 @@ const ScenarioInput = ({ const [, case_commodity, id] = objectId.split("-"); let segmentAnswer = {}; + // handle grouped diversified value if (id === "diversified") { const childrens = commodityQuestions .find((cq) => cq.commodity_type === "diversified") @@ -592,12 +594,15 @@ const Scenario = ({ ...x, case_commodity: diversified.case_commodity, commodity_name: diversified.commodity_name, + childrens: x.childrens, })); return [...commodities, ...diversified].map((q) => ({ questionId: q.id, text: q.text, questionType: q.question_type, caseCommodityId: q.case_commodity, + commodityName: q.commodity_name, + childrens: q?.childrens || [], })); }, [commodityQuestions]); @@ -833,13 +838,24 @@ const Scenario = ({ // current data const current = dashboardData.find((dd) => dd.id === selectedSegment); const data = outcomeIndicator.map((ind) => { - let res = { title: ind.name }; + let res = { id: ind.key, title: ind.name }; if (ind.key === "income_driver") { res = { ...res, current: "-", }; const currentDriverValues = outcomeDriverQuestions.map((q) => { + // handle grouped diversified income + if (q.questionType === "diversified") { + const temp = q.childrens.map( + (c) => + current?.answers?.find((a) => a.questionId === c.id)?.value || 0 + ); + return { + ...q, + value: temp.reduce((res, cur) => res + cur, 0), + }; + } return { ...q, value: @@ -1116,6 +1132,7 @@ const Scenario = ({ onChange={setSelectedSegment} />