diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/IndicatorProgressChart.tsx b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/IndicatorProgressChart.tsx index f6b15e9dbed..be4eba599d0 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/IndicatorProgressChart.tsx +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/IndicatorProgressChart.tsx @@ -81,6 +81,7 @@ const IndicatorProgressChart: React.FC = (props: In targetValue: apiData.targetValue, actualValue: actualValue === 0 ? apiData.baseValue : actualValue }); + console.log("Progress Value: ", progress); setProgressValue(progress); } diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/sections/IndicatorByProgram.tsx b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/sections/IndicatorByProgram.tsx index 3da934f8896..cfc4da379d1 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/sections/IndicatorByProgram.tsx +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/components/sections/IndicatorByProgram.tsx @@ -29,7 +29,8 @@ const IndicatorByProgram: React.FC = (props) => const handleIndicatorChange = (value: number) => { if (indicators && indicators.length > 0) { setSelectedIndicator(null); - const findIndicator = indicators.find((item: any) => item.id === value); + const findIndicator = indicators.find((item: any) => item.id == value); + console.log("Find Indicator: ", findIndicator); if (findIndicator) { setSelectedIndicator(findIndicator); } @@ -46,6 +47,7 @@ const IndicatorByProgram: React.FC = (props) => }, [indicators]); useEffect(() => { + console.log("Selected Indicator ID: ", selectedIndicatorId); handleIndicatorChange(selectedIndicatorId as number) }, [selectedIndicatorId]); @@ -104,7 +106,7 @@ const IndicatorByProgram: React.FC = (props) => {selectedIndicator && ( = (props) => { } // eslint-disable-next-line react-hooks/exhaustive-deps }, [indicatorsByProgramReducer.data]); - + // @ts-ignore return (
@@ -68,7 +68,7 @@ const LeftSection: React.FC = (props) => {
- = (props) => { +const ProgramProgressComponent: React.FC = (props) => { const { translations, level1Child, @@ -254,4 +254,4 @@ const ProgramConfiguration: React.FC = (props) => { ) } -export default ProgramConfiguration; +export default ProgramProgressComponent; diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/types.ts b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/types.ts index 1c47f282b7a..9b34c07bd24 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/types.ts +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/types.ts @@ -44,13 +44,16 @@ export type InitialState = { export interface ActualValue { year: string | number; + date: string; value: number } export interface YearValues { actualValues: ActualValue []; baseValue: number; + baseValueDate: string; targetValue: number; + targetValueDate: string; indicatorId: number; } diff --git a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/utils/chart.ts b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/utils/chart.ts index dd3daa9efb6..27ee5f093e6 100644 --- a/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/utils/chart.ts +++ b/amp/TEMPLATE/reampv2/packages/reampv2-app/src/modules/ndddashboard/medashboard/utils/chart.ts @@ -2,16 +2,15 @@ import {ActualValue, DefaultTranslations, LineChartData, SectorReport, YearValue import {printChart} from "../../../sscdashboard/utils/PrintUtils"; import { BASE_VALUE, - BASE_VALUE_COLOR, CURRENT_VALUE, CURRENT_VALUE_COLOR, + BASE_VALUE_COLOR, + CURRENT_VALUE, + CURRENT_VALUE_COLOR, DEFAULT_REPORTING_PERIOD, + SECTOR_COLOR, TARGET_VALUE, - TARGET_VALUE_COLOR, - SECTOR_COLOR + TARGET_VALUE_COLOR } from "../../utils/constants"; -import React from "react"; import {DataType} from "../components/charts/BarChart"; -import _ from 'lodash'; -import {DateUtil} from "../../../admin/indicator_manager/utils/dateFn"; import dayjs from "dayjs"; interface GaugeUtils { @@ -75,6 +74,11 @@ class ChartUtils { return actualValue ? actualValue.value : 0; } + public static sumActualValues = (actualValues: ActualValue []) => { + if (!actualValues || actualValues.length === 0) return 0; + + return actualValues.reduce((acc, curr) => acc + curr.value, 0); + } public static getMaxActualValue = (actualValues: ActualValue []) => { if (!actualValues || actualValues.length === 0) return 0; @@ -88,7 +92,7 @@ class ChartUtils { if (!curr.actualValues || curr.actualValues.length === 0) { acc.actualValue += curr.baseValue; } - acc.actualValue += ChartUtils.getActualValueForCurrentYear(curr.actualValues); + acc.actualValue += ChartUtils.sumActualValues(curr.actualValues); acc.targetValue += curr.targetValue; acc.baseValue += curr.baseValue; return acc; @@ -167,53 +171,86 @@ class ChartUtils { const finalDataSet: DataType [] = []; - if (data) { - let aggregateValue = { - baseValue: 0, - targetValue: 0, - actualValue: 0 - }; - - if (Array.isArray(data)) { - aggregateValue = ChartUtils.computeAggregateValues(data); - } else { - aggregateValue = ChartUtils.computeAggregateValues([data]); - } - - const year = new Date().getFullYear(); - if (aggregateValue.baseValue) { - const baseData = { - id: translations['amp.ndd.dashboard:me-baseline'], - value: aggregateValue.baseValue, - label: `${translations['amp.ndd.dashboard:me-baseline']} ${year}`, - color: BASE_VALUE_COLOR - } - - finalDataSet.push(baseData); - } - - if (aggregateValue.actualValue) { - const actualData = { - id: translations['amp.ndd.dashboard:me-current'], - value: aggregateValue.actualValue, - label: `${translations['amp.ndd.dashboard:me-current']} ${year}`, - color: CURRENT_VALUE_COLOR - }; + const collectItems = Array.isArray(data) ? data : (data ? [data] : []); - finalDataSet.push(actualData); + // helper to parse dd/MM/yyyy or ISO to Date + const parseDate = (d?: string): Date | null => { + if (!d) return null; + const parts = d.split('/'); + if (parts.length === 3 && parts[2].length === 4) { + const [dd, MM, yyyy] = parts; + const dateObj = new Date(Number(yyyy), Number(MM) - 1, Number(dd)); + return isNaN(dateObj.getTime()) ? null : dateObj; } + const iso = new Date(d); + return isNaN(iso.getTime()) ? null : iso; + }; + // new helper: extract only the year string + const extractYear = (d?: string): string | undefined => { + if (!d) return undefined; + if (/^\d{4}$/.test(d)) return d; // already a year + const parsed = parseDate(d); + return parsed ? parsed.getFullYear().toString() : undefined; + }; + const getLatestYearForActualValues = (values: ActualValue[]): number | undefined => { + if (!values || values.length === 0) return undefined; + const years = values.map(v => { return Number(v.year); }).filter((y): y is number => y !== null); + if (years.length === 0) return undefined; + return Math.max(...years); + } + const getMaxDateString = (dates: string[]): string | undefined => { + const mapped = dates.map(d => ({ raw: d, date: parseDate(d) })) + .filter(o => o.date !== null) as { raw: string; date: Date }[]; + if (mapped.length === 0) return undefined; + mapped.sort((a,b) => a.date.getTime() - b.date.getTime()); + return mapped[mapped.length - 1].raw; // keep original formatting + }; + + // aggregate numeric values (existing logic) + let aggregateValue = { baseValue: 0, targetValue: 0, actualValue: 0 }; + if (collectItems.length > 0) { + aggregateValue = ChartUtils.computeAggregateValues(collectItems as YearValues[]); + } - if (aggregateValue.targetValue) { - const targetData = { - id: translations['amp.ndd.dashboard:me-target'], - value: aggregateValue.targetValue, - label: `${translations['amp.ndd.dashboard:me-target']} ${year}`, - color: TARGET_VALUE_COLOR - }; + // collect date strings + const baseDates: string[] = collectItems.map((i: YearValues) => i.baseValueDate).filter(Boolean); + const targetDates: string[] = collectItems.map((i: YearValues) => i.targetValueDate).filter(Boolean); + const maxBaseDateStr = getMaxDateString(baseDates); + const maxTargetDateStr = getMaxDateString(targetDates); + const year = new Date().getFullYear(); + const maxActualYearStr = getLatestYearForActualValues(collectItems.flatMap(i => i.actualValues)); + + const baseYearLabel = extractYear(maxBaseDateStr) || year; + const targetYearLabel = extractYear(maxTargetDateStr) || year; + + if (aggregateValue.baseValue) { + const baseData = { + id: translations['amp.ndd.dashboard:me-baseline'], + value: aggregateValue.baseValue, + label: `${translations['amp.ndd.dashboard:me-baseline']} ${baseYearLabel}`.trim(), + color: BASE_VALUE_COLOR + }; + finalDataSet.push(baseData); + } - finalDataSet.push(targetData); - } + if (aggregateValue.actualValue) { + const actualData = { + id: translations['amp.ndd.dashboard:me-current'], + value: aggregateValue.actualValue, + label: `${translations['amp.ndd.dashboard:me-current']} ${maxActualYearStr || year}`.trim(), + color: CURRENT_VALUE_COLOR + }; + finalDataSet.push(actualData); + } + if (aggregateValue.targetValue) { + const targetData = { + id: translations['amp.ndd.dashboard:me-target'], + value: aggregateValue.targetValue, + label: `${translations['amp.ndd.dashboard:me-target']} ${targetYearLabel}`.trim(), + color: TARGET_VALUE_COLOR + }; + finalDataSet.push(targetData); } return finalDataSet; diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/DocumentReadyBehavior.java b/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/DocumentReadyBehavior.java index c554971b452..6d5d958d8c3 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/DocumentReadyBehavior.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/DocumentReadyBehavior.java @@ -31,6 +31,7 @@ public void renderHead(Component component, IHeaderResponse response) { variables.put("onepagerMode", activityFormOnePager); variables.put("onepagerPath", "/" + OnePagerConst.ONEPAGER_URL_PREFIX + "/" + OnePagerConst.ONEPAGER_URL_PARAMETER_ACTIVITY + "/"); variables.put("isTabView",FeaturesUtil.getGlobalSettingValueBoolean(GlobalSettingsConstants.ACTIVITY_FORM_FUNDING_SECTION_DESIGN)); + variables.put("isMeTabView",FeaturesUtil.getGlobalSettingValueBoolean(GlobalSettingsConstants.IS_ME_TABVIEW)); variables.put("isRtl", SiteUtils.isEffectiveLangRTL()); PackageTextTemplate ptt = new PackageTextTemplate(DocumentReadyBehavior.class, JS_FILE_NAME); diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/documentReady.js b/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/documentReady.js index a19056ff434..84a7aca5236 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/documentReady.js +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/behaviors/documentReady.js @@ -1,6 +1,7 @@ enableComputateVisibleSections = false; onepagerMode = ${onepagerMode}; isTabView = ${isTabView}; +var isMeTabView = ${isMeTabView}; var isRtl = ${isRtl}; //the distance between the content body and the menu should be 40px @@ -208,7 +209,6 @@ function pageLeaveConfirmationEnabler(){ } function switchTabs(lastIndex) { - if (isTabView) { $('div[data-is_tab=true]').each(function(index) { $(this).appendTo("#theContent"); }); @@ -239,11 +239,9 @@ function switchTabs(lastIndex) { } }); loader.insert(); - } } function indicatorTabs(lastIndex) { - if (isTabView) { $('div[data-is_location_tab=true]').each(function(index) { $(this).appendTo("#theLocationIndicatorContent"); }); @@ -274,7 +272,6 @@ function indicatorTabs(lastIndex) { } }); loader.insert(); - } } function getLeftPositionOfRightMenu(isAbsolutePosition) { @@ -319,6 +316,9 @@ $(document).ready(function(){ pageLeaveConfirmationEnabler(); if(isTabView){ switchTabs(); + } + if (isMeTabView) + { indicatorTabs(); } diff --git a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpMEFormSectionFeature.java b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpMEFormSectionFeature.java index 3477f8c5fae..b641965e146 100644 --- a/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpMEFormSectionFeature.java +++ b/amp/src/main/java/org/dgfoundation/amp/onepager/components/features/sections/AmpMEFormSectionFeature.java @@ -19,7 +19,9 @@ import org.dgfoundation.amp.onepager.util.AttributePrepender; import org.dgfoundation.amp.onepager.yui.AmpAutocompleteFieldPanel; import org.digijava.module.aim.dbentity.*; +import org.digijava.module.aim.helper.GlobalSettingsConstants; import org.digijava.module.aim.util.DbUtil; +import org.digijava.module.aim.util.FeaturesUtil; import java.util.ArrayList; import java.util.HashSet; @@ -43,7 +45,7 @@ public class AmpMEFormSectionFeature extends AmpFormSectionFeaturePanel { private Map locationIndicatorItems = new TreeMap<>(); - private boolean isTabsView = true; + private boolean isTabsView = FeaturesUtil.getGlobalSettingValueBoolean(GlobalSettingsConstants.IS_ME_TABVIEW); final List locations; @@ -107,19 +109,21 @@ protected void populateItem(org.apache.wicket.markup.html.list.ListItem item) { AmpMEItemFeaturePanel indicatorLoc = null; try { - indicatorLoc = new AmpMEItemFeaturePanel("indicatorLocation", "ME Item Location", item.getModel(), am, locations - ); + indicatorLoc = new AmpMEItemFeaturePanel("indicatorLocation", "ME Item Location", item.getModel(), am, locations); } catch (Exception e) { throw new RuntimeException(e); } indicatorLoc.setTabIndex(item.getIndex()); + // Apply tab id and data attribute to the ListItem's corresponding markup element (the placeholder div), + // because documentReady.js searches for div[data-is_location_tab=true] item.add(new AttributePrepender("data-is_location_tab", new Model("true"), "")); - locationIndicatorItems.put(item.getModelObject(), indicatorLoc); + item.setOutputMarkupId(true); + item.setMarkupId("tab" + (item.getIndex() + 1)); + locationIndicatorItems.put(item.getModelObject(), indicatorLoc); item.add(indicatorLoc); } - }; indicatorLocationList.setOutputMarkupId(true); add(indicatorLocationList); diff --git a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/dashboards/services/MeService.java b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/dashboards/services/MeService.java index 553a2596e37..731ecfd0116 100644 --- a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/dashboards/services/MeService.java +++ b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/dashboards/services/MeService.java @@ -33,13 +33,7 @@ import org.digijava.kernel.ampapi.endpoints.util.FilterUtils; import org.digijava.kernel.exception.DgException; import org.digijava.kernel.persistence.PersistenceManager; -import org.digijava.module.aim.dbentity.AmpActivityProgramSettings; -import org.digijava.module.aim.dbentity.AmpClassificationConfiguration; -import org.digijava.module.aim.dbentity.AmpIndicator; -import org.digijava.module.aim.dbentity.AmpSector; -import org.digijava.module.aim.dbentity.AmpSectorScheme; -import org.digijava.module.aim.dbentity.AmpTheme; -import org.digijava.module.aim.dbentity.IndicatorActivity; +import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.util.IndicatorUtil; import org.digijava.module.aim.util.ProgramUtil; import org.digijava.module.aim.util.SectorUtil; @@ -52,26 +46,21 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.text.SimpleDateFormat; +import java.util.*; import java.time.Year; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static javax.ws.rs.core.Response.Status.*; public class MeService { protected static final Logger logger = Logger.getLogger(MeService.class); + private static final ThreadLocal BASE_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("dd/MM/yyyy")); public static List getProgramConfiguration() { List settings = ProgramUtil.getAmpActivityProgramSettingsList(true); @@ -105,29 +94,33 @@ public List getIndicatorsBySector(Long sectorId) { } public List getIndicatorValuesByProgramId(Long programId, - SettingsAndFiltersParameters params) { + SettingsAndFiltersParameters params) { - List indicatorsByProgram = getAllAmpIndicators().stream() - .filter(indicator -> indicator.getProgram() != null) - .filter(indicator -> indicator.getProgram().getAmpThemeId().equals(programId)) - .collect(Collectors.toList()); + + Set indicatorsByProgram = new HashSet<>(); + try { + indicatorsByProgram = IndicatorUtil.getThemeIndicators(programId); + if (indicatorsByProgram == null) { + return Collections.emptyList(); + } + } catch (Exception e) { + throw new ApiRuntimeException(INTERNAL_SERVER_ERROR, + ApiError.toError("Error loading indicators for program with id " + programId)); + } Map> indicatorsWithYearValues = getAllIndicatorYearValuesWithActualValues(params); return indicatorsByProgram.stream() - .map(indicator -> getIndicatorYearValues(indicator, indicatorsWithYearValues)) + .map(indicator -> getIndicatorYearValues(indicator, indicatorsWithYearValues, programId)) .collect(Collectors.toList()); } public IndicatorYearValues getIndicatorYearValuesByIndicatorId(Long indicatorId, SettingsAndFiltersParameters params) { - AmpIndicator existingIndicator = getAllAmpIndicators().stream() - .filter(indicator -> indicator.getIndicatorId().equals(indicatorId)) - .findFirst() - .orElse(null); + AmpIndicator existingIndicator =PersistenceManager.getRequestDBSession().get(AmpIndicator.class, indicatorId); - int yearsCount = Integer.valueOf(params.getSettings().get("yearCount").toString()); + int yearsCount = Integer.parseInt(params.getSettings().get("yearCount").toString()); if (yearsCount < 5) { yearsCount = 5; @@ -139,15 +132,15 @@ public IndicatorYearValues getIndicatorYearValuesByIndicatorId(Long indicatorId, } Map> indicatorsWithYearValues = getAllIndicatorYearValuesWithActualValues(params); - Map baseTargetValues = getBaseTargetValues(params); +// Map baseTargetValues = getBaseTargetValues(params); - return getIndicatorYearValues(existingIndicator, indicatorsWithYearValues, yearsCount, baseTargetValues); + return getIndicatorYearValues(existingIndicator, indicatorsWithYearValues, yearsCount); } public List getIndicatorYearValuesByIndicatorCountryProgramId(SettingsAndFiltersParameters params) { List programIndicatorValues = new ArrayList<>(); Map baseTargetValues = getBaseTargetValues(params); - int yearsCount = Integer.valueOf(params.getSettings().get("yearCount").toString()); + int yearsCount = Integer.parseInt(params.getSettings().get("yearCount").toString()); if (yearsCount < 5) { yearsCount = 5; @@ -241,25 +234,108 @@ private String extractIndicatorNumber(String indicatorName) { } private IndicatorYearValues getIndicatorYearValues(final AmpIndicator indicator, - final Map> indicatorsWithYearValues) { - BigDecimal baseValue = BigDecimal.ZERO; - BigDecimal targetValue = BigDecimal.ZERO; + final Map> indicatorsWithYearValues, Long programId) { + BigDecimal baseValue; + BigDecimal targetValue; + String baseValueDate = null; + String targetValueDate = null; + IndicatorConnectionValueWithDate baseValueWithDate = getValueFromIndicatorConnection(indicator, programId, AmpIndicatorValue.BASE, true); + baseValue = baseValueWithDate.value; + java.sql.Timestamp baseValueTimestamp = baseValueWithDate.valueDate; + if (baseValueTimestamp != null) { + baseValueDate = BASE_DATE_FORMAT.get().format(baseValueTimestamp); + } + IndicatorConnectionValueWithDate targetValueWithDate = getValueFromIndicatorConnection(indicator, programId, AmpIndicatorValue.TARGET, true); + targetValue = targetValueWithDate.value; + java.sql.Timestamp targetValueTimestamp = targetValueWithDate.valueDate; + if (targetValueTimestamp != null) { + targetValueDate = BASE_DATE_FORMAT.get().format(targetValueTimestamp); + } - if (indicator.getBaseValue() != null && indicator.getBaseValue().getValue() != null) { - baseValue = BigDecimal.valueOf(indicator.getBaseValue().getValue()); + if (baseValue.equals(BigDecimal.ZERO)) { + if (indicator.getBaseValue() != null) { + if (indicator.getBaseValue().getValue() != null) { + baseValue = BigDecimal.valueOf(indicator.getBaseValue().getValue()); + } + if (indicator.getBaseValue().getValueDate() != null) { + baseValueDate = BASE_DATE_FORMAT.get().format(indicator.getBaseValue().getValueDate()); + } + } } - if (indicator.getTargetValue() != null && indicator.getTargetValue().getValue() != null) { - targetValue = BigDecimal.valueOf(indicator.getTargetValue().getValue()); + if (targetValue.equals(BigDecimal.ZERO)) { + if (indicator.getTargetValue() != null) { + if (indicator.getTargetValue().getValue() != null) { + targetValue = BigDecimal.valueOf(indicator.getTargetValue().getValue()); + } + if (indicator.getTargetValue().getValueDate() != null) { + targetValueDate = BASE_DATE_FORMAT.get().format(indicator.getTargetValue().getValueDate()); + } + } } List yearValues = indicatorsWithYearValues.get(indicator.getIndicatorId()); - if (yearValues == null) { yearValues = Collections.emptyList(); } - return new IndicatorYearValues(indicator, baseValue, yearValues, targetValue); + return new IndicatorYearValues(indicator, baseValue, baseValueDate, yearValues, targetValue, targetValueDate); + } + + public static class IndicatorConnectionValueWithDate { + public final BigDecimal value; + public final java.sql.Timestamp valueDate; + public IndicatorConnectionValueWithDate(BigDecimal value, java.sql.Timestamp valueDate) { + this.value = value; + this.valueDate = valueDate; + } + } + + private IndicatorConnectionValueWithDate getValueFromIndicatorConnection(AmpIndicator indicator, Long programId, int valueType, boolean singleRecord) { + AtomicReference value = new AtomicReference<>(null); + AtomicReference valueDate = new AtomicReference<>(null); + Session session = PersistenceManager.getSession(); + StringBuilder sql = new StringBuilder(); + if (singleRecord) { + sql.append("SELECT iv.value, iv.value_date FROM amp_indicator_values iv "); + sql.append("JOIN amp_indicator_connection ic ON iv.ind_connect_id = ic.id "); + sql.append("WHERE ic.indicator_id = ? AND iv.value_type = ? "); + if (programId != null) { + sql.append("AND ic.theme_id = ? "); + } + sql.append("ORDER BY iv.value_date DESC "); + sql.append("LIMIT 1"); + } else { + sql.append("SELECT SUM(latest.value) AS value, MAX(latest.value_date) AS value_date FROM ("); + sql.append("SELECT iv.value, iv.value_date "); + sql.append("FROM amp_indicator_values iv "); + sql.append("JOIN amp_indicator_connection ic ON iv.ind_connect_id = ic.id "); + sql.append("WHERE ic.indicator_id = ? AND iv.value_type = ? "); + sql.append("AND iv.value_date = ("); + sql.append("SELECT MAX(iv2.value_date) FROM amp_indicator_values iv2 "); + sql.append("JOIN amp_indicator_connection ic2 ON iv2.ind_connect_id = ic2.id "); + sql.append("WHERE ic2.theme_id = ic.theme_id AND ic2.indicator_id = ic.indicator_id AND iv2.value_type = iv.value_type"); + sql.append(")"); + sql.append(") latest"); + } + + session.doWork(connection -> { + try (java.sql.PreparedStatement ps = connection.prepareStatement(sql.toString())) { + ps.setLong(1, indicator.getIndicatorId()); + ps.setInt(2, valueType); + if (programId != null) { + ps.setLong(3, programId); + } + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + value.set(rs.getBigDecimal("value")); + valueDate.set(rs.getTimestamp("value_date")); + } + } + } + }); + BigDecimal safeValue = value.get() == null ? BigDecimal.ZERO : value.get(); + return new IndicatorConnectionValueWithDate(safeValue, valueDate.get()); } private IndicatorYearValues getIndicatorYearValues(final AmpIndicator indicator, @@ -298,6 +374,77 @@ private IndicatorYearValues getIndicatorYearValues(final AmpIndicator indicator, return new IndicatorYearValues(indicator, baseValue, yearValues, targetValue); } + /** + * + * @param indicator + * @param indicatorsWithYearValues + * @param yearsCount + * @return + * NOTE: This method first tries to get the base and target values from the indicator itself. If those are not set (i.e., they are null or zero), it then attempts to retrieve + * the values from the indicator connections. This ensures that if the base and target values are not explicitly set on the indicator, they can still be obtained from related connections. + * @jdanquin + */ + private IndicatorYearValues getIndicatorYearValues(final AmpIndicator indicator, + final Map> indicatorsWithYearValues, int yearsCount) { + BigDecimal baseValue = BigDecimal.ZERO; + BigDecimal targetValue = BigDecimal.ZERO; + String baseValueDate = null; + String targetValueDate = null; + + if (indicator.getBaseValue() != null && indicator.getBaseValue().getValue() != null) { + baseValue = BigDecimal.valueOf(indicator.getBaseValue().getValue()); + } + if (indicator.getBaseValue() != null && indicator.getBaseValue().getValueDate() != null) { + baseValueDate = BASE_DATE_FORMAT.get().format(indicator.getBaseValue().getValueDate()); + } + + + if (indicator.getTargetValue() != null && indicator.getTargetValue().getValue() != null) { + targetValue = BigDecimal.valueOf(indicator.getTargetValue().getValue()); + } + if (indicator.getTargetValue() != null && indicator.getTargetValue().getValueDate() != null) { + targetValueDate = BASE_DATE_FORMAT.get().format(indicator.getTargetValue().getValueDate()); + } + + if (baseValue.equals(BigDecimal.ZERO)){ + IndicatorConnectionValueWithDate baseValueWithDate = getValueFromIndicatorConnection(indicator, null,AmpIndicatorValue.BASE, false); + baseValue = baseValueWithDate.value; + java.sql.Timestamp baseValueTimestamp = baseValueWithDate.valueDate; + if (baseValueTimestamp != null) { + baseValueDate = BASE_DATE_FORMAT.get().format(baseValueTimestamp); + } + } + if (targetValue.equals(BigDecimal.ZERO)){ + IndicatorConnectionValueWithDate targetValueWithDate = getValueFromIndicatorConnection(indicator, null, AmpIndicatorValue.TARGET,false); + targetValue = targetValueWithDate.value; + java.sql.Timestamp targetValueTimestamp = targetValueWithDate.valueDate; + if (targetValueTimestamp != null) { + targetValueDate = BASE_DATE_FORMAT.get().format(targetValueTimestamp); + } + } + + + List yearValues = indicatorsWithYearValues.get(indicator.getIndicatorId()); + + if (yearValues == null) { + yearValues = Collections.emptyList(); + } + + if (yearValues.size() > yearsCount) { + yearValues = yearValues.subList(0, yearsCount); + } else if (yearValues.size() < yearsCount) { + List newYearValues = new ArrayList<>(yearsCount); + newYearValues.addAll(yearValues); + + for (int i = yearValues.size(); i < yearsCount; i++) { + newYearValues.add(new YearValue(Year.now().getValue() - i, BigDecimal.ZERO)); + } + yearValues = newYearValues; + } + + return new IndicatorYearValues(indicator, baseValue,baseValueDate, yearValues, targetValue, targetValueDate); + } + public Map> getAllIndicatorYearValuesWithActualValues(SettingsAndFiltersParameters params) { GeneratedReport generatedReport = runIndicatorReport(params); Map> data = new HashMap<>(); @@ -306,11 +453,12 @@ public Map> getAllIndicatorYearValuesWithActualValues(Sett ? Collections.emptyList() : generatedReport.reportContents.getChildren(); - Map indicatorById = getAllAmpIndicators().stream() - .collect(Collectors.toMap(AmpIndicator::getIndicatorId, Function.identity())); + //avoid loading many items to memory +// Map indicatorById = getAllAmpIndicators().stream() +// .collect(Collectors.toMap(AmpIndicator::getIndicatorId, Function.identity())); for (ReportArea area : children) { - AmpIndicator indicator = indicatorById.get(area.getOwner().id); + AmpIndicator indicator = PersistenceManager.getRequestDBSession().get(AmpIndicator.class, area.getOwner().id); List actualValues = new ArrayList<>(); for (Map.Entry entry : area.getContents().entrySet()) { diff --git a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/indicator/IndicatorYearValues.java b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/indicator/IndicatorYearValues.java index a25be56222a..5575aec28b4 100644 --- a/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/indicator/IndicatorYearValues.java +++ b/amp/src/main/java/org/digijava/kernel/ampapi/endpoints/indicator/IndicatorYearValues.java @@ -17,10 +17,13 @@ public class IndicatorYearValues { private AmpIndicator indicator; private BigDecimal baseValue; + private String baseValueDate; private List actualValues; + private BigDecimal targetValue; + private String targetValueDate; private String indicatorName; @@ -28,8 +31,22 @@ public IndicatorYearValues(AmpIndicator indicator, BigDecimal baseValue, List actualValues, + BigDecimal targetValue, String targetValueDate) { + this.indicator = indicator; + this.baseValue = baseValue; + this.baseValueDate = baseValueDate; + this.indicatorName = indicator != null ? indicator.getName() : null; + this.actualValues = actualValues; + this.targetValue = targetValue; + this.targetValueDate = targetValueDate; + } public IndicatorYearValues() { @@ -42,6 +59,12 @@ public AmpIndicator getIndicator() { public BigDecimal getBaseValue() { return baseValue; } + public String getBaseValueDate() { + return baseValueDate; + } + public void setBaseValueDate(String baseValueDate) { + this.baseValueDate = baseValueDate; + } public List getActualValues() { return actualValues; @@ -58,4 +81,12 @@ public String getIndicatorName() { public void setIndicatorName(String indicatorName) { this.indicatorName = indicatorName; } + + public void setTargetValueDate(String targetValueDate) { + this.targetValueDate = targetValueDate; + } + + public String getTargetValueDate() { + return targetValueDate; + } } diff --git a/amp/src/main/java/org/digijava/module/aim/action/AddEditData.java b/amp/src/main/java/org/digijava/module/aim/action/AddEditData.java index c603640a71e..768d61c5088 100644 --- a/amp/src/main/java/org/digijava/module/aim/action/AddEditData.java +++ b/amp/src/main/java/org/digijava/module/aim/action/AddEditData.java @@ -40,7 +40,7 @@ public ActionForward execute(ActionMapping mapping, ActionForm form, Long parentId=Long.valueOf(parent); IndicatorTheme connection=IndicatorUtil.getConnectionToTheme(parentId); themeForm.setIndicatorName(connection.getIndicator().getName()); - if (connection.getValues()!=null && connection.getValues().size()>0){ + if (connection.getValues()!=null && !connection.getValues().isEmpty()){ List sortedIndicatorValues = new ArrayList(connection.getValues()); Collections.sort(sortedIndicatorValues,new IndicatorValuesComparator()); List indValuesList=new ArrayList(); @@ -117,10 +117,7 @@ public ActionForward execute(ActionMapping mapping, ActionForm form, themeForm.setPrgIndValues(indValues); }else if(event!=null && event.equals("save")){ if (themeForm.getParentId() != null) { - for (Iterator indValIter = indValues.iterator(); indValIter - .hasNext();) { - AmpPrgIndicatorValue indVal = (AmpPrgIndicatorValue) indValIter - .next(); + for (AmpPrgIndicatorValue indVal : indValues) { if (indVal.getIndicatorValueId() != null && (indVal.getIndicatorValueId().longValue() < 0)) { // ProgramUtil.deletePrgIndicatorValueById(themeForm.getParentId(),indVal.getIndicatorValueId()); @@ -139,17 +136,16 @@ public ActionForward execute(ActionMapping mapping, ActionForm form, IndicatorTheme connection=IndicatorUtil.getConnectionToTheme(themeForm.getParentId()); if (connection!=null){ connection.getValues().clear(); - for (Iterator indValIter = indValues.iterator(); indValIter.hasNext();) { - AmpPrgIndicatorValue prgValue = (AmpPrgIndicatorValue) indValIter.next(); - if(prgValue.getCreationDate()!=null && !prgValue.getCreationDate().equals("") && prgValue.getValAmount()!=null && !prgValue.getValAmount().equals("")){ - AmpIndicatorValue value=new AmpIndicatorValue(); + for (AmpPrgIndicatorValue prgValue : indValues) { + if (prgValue.getCreationDate() != null && !prgValue.getCreationDate().equals("") && prgValue.getValAmount() != null && !prgValue.getValAmount().equals("")) { + AmpIndicatorValue value = new AmpIndicatorValue(); value.setValue(prgValue.getValAmount()); value.setValueDate(DateConversion.getDateForIndicator(prgValue.getCreationDate())); value.setValueType(prgValue.getValueType()); value.setLocation(prgValue.getLocation()); value.setIndicatorConnection(connection); connection.getValues().add(value); - } + } } try{ IndicatorUtil.updateThemeConnection(connection); diff --git a/amp/src/main/java/org/digijava/module/aim/dbentity/AmpTheme.java b/amp/src/main/java/org/digijava/module/aim/dbentity/AmpTheme.java index 5a9b559b08b..8753d541a4c 100644 --- a/amp/src/main/java/org/digijava/module/aim/dbentity/AmpTheme.java +++ b/amp/src/main/java/org/digijava/module/aim/dbentity/AmpTheme.java @@ -222,9 +222,8 @@ public boolean equals(Object obj) { } @Override - public int hashCode() - { - return this.ampThemeId == null ? null : this.ampThemeId.hashCode(); + public int hashCode() { + return ampThemeId != null ? ampThemeId.hashCode() : 0; } public void setLeadAgency (String leadAgency) { diff --git a/amp/src/main/java/org/digijava/module/aim/helper/GlobalSettingsConstants.java b/amp/src/main/java/org/digijava/module/aim/helper/GlobalSettingsConstants.java index 56e392a3de3..5ea20b10146 100644 --- a/amp/src/main/java/org/digijava/module/aim/helper/GlobalSettingsConstants.java +++ b/amp/src/main/java/org/digijava/module/aim/helper/GlobalSettingsConstants.java @@ -205,6 +205,7 @@ public class GlobalSettingsConstants { public static final String COUNTRY_LONGITUDE = "Country Longitude"; public static final String ACTIVITY_FORM_FUNDING_SECTION_DESIGN = "Activity Form - Funding Section - Tab View"; + public static final String IS_ME_TABVIEW = "Activity Form - M&E is TabView"; public static final String MAX_LOCATIONS_ICONS = "Show icons for Project Sites for locations up to"; diff --git a/amp/src/main/java/org/digijava/module/aim/util/IndicatorUtil.java b/amp/src/main/java/org/digijava/module/aim/util/IndicatorUtil.java index d033a5b7db8..3c7c76663cc 100644 --- a/amp/src/main/java/org/digijava/module/aim/util/IndicatorUtil.java +++ b/amp/src/main/java/org/digijava/module/aim/util/IndicatorUtil.java @@ -628,7 +628,7 @@ public static void updateThemeConnection(IndicatorTheme connection) throws DgExc try { //beginTransaction(); Set indValues=connection.getValues(); - if(indValues!=null && indValues.size()>0){ + if(indValues!=null && !indValues.isEmpty()){ for (AmpIndicatorValue indicatorValue : indValues) { AmpLocation location = indicatorValue.getLocation(); if (location != null) { diff --git a/amp/src/main/resources/xmlpatches/4.0/AMP-31053-Add-TabView-Setting-For-M&E.xml b/amp/src/main/resources/xmlpatches/4.0/AMP-31053-Add-TabView-Setting-For-M&E.xml new file mode 100644 index 00000000000..096bd684633 --- /dev/null +++ b/amp/src/main/resources/xmlpatches/4.0/AMP-31053-Add-TabView-Setting-For-M&E.xml @@ -0,0 +1,17 @@ + + + AMP-31053 + bmokandu + TabView Setting for ME. + + + +