From 30c6a72ef08ff0f7de54cee00cf304a042e0a198 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 26 Apr 2026 21:33:55 +0300 Subject: [PATCH 01/20] Add funding columns to this old GGW codebase --- .../amp/newreports/ReportsDashboard.java | 97 +++- .../message/jobs/AmpDonorFundingJob.java | 458 +++++++++++++++--- 2 files changed, 466 insertions(+), 89 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java b/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java index 95ae7b79ebf..14be3904c3a 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java @@ -4,21 +4,31 @@ public class ReportsDashboard { private String donorAgency; + private String implementingAgency; + private String responsibleOrganization; private String pillar; private String implementationLevel; - - private String country; + private String procurementSystem; + private String location; private String status; private String reportingSystem; private String typeOfAssistance; private String currency; - private String year; + //private String year; private BigDecimal actualCommitment; private BigDecimal actualDisbursement; + private Long activityCount; + // Comma separated AMP activity IDs aggregated under the current hierarchy path + private String activityIds; + private String projectTitle; + // Translations for field labels: List of maps, each map contains "label" key and locale codes as keys + private java.util.List> translations; + private String secondarySector; public ReportsDashboard() { this.actualCommitment = BigDecimal.ZERO; this.actualDisbursement = BigDecimal.ZERO; + this.activityCount = 0L; } @@ -30,6 +40,14 @@ public void setDonorAgency(String donorAgency) { this.donorAgency = donorAgency; } + public String getImplementingAgency() { + return implementingAgency; + } + + public void setImplementingAgency(String implementingAgency) { + this.implementingAgency = implementingAgency; + } + public String getPillar() { return pillar; } @@ -38,14 +56,23 @@ public void setPillar(String pillar) { this.pillar = pillar; } - public String getYear() { - return year; + public String getActivityIds() { + return activityIds; } - public void setYear(String year) { - this.year = year; + public void setActivityIds(String activityIds) { + this.activityIds = activityIds; } + /* + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + */ public BigDecimal getActualCommitment() { return actualCommitment; } @@ -54,12 +81,12 @@ public void setActualCommitment(BigDecimal actualCommitment) { this.actualCommitment = actualCommitment; } - public String getCountry() { - return country; + public String getLocation() { + return location; } - public void setCountry(String country) { - this.country = country; + public void setLocation(String location) { + this.location = location; } public String getImplementationLevel() { @@ -118,4 +145,50 @@ public String getTypeOfAssistance() { public void setTypeOfAssistance(String typeOfAssistance) { this.typeOfAssistance = typeOfAssistance; } -} + + public Long getActivityCount() { + return activityCount; + } + + public void setActivityCount(Long activityCount) { + this.activityCount = activityCount; + } + public String getProcurementSystem() { + return procurementSystem; + } + public void setProcurementSystem(String procurementSystem) { + this.procurementSystem = procurementSystem; + } + + public String getResponsibleOrganization() { + return responsibleOrganization; + } + + public void setResponsibleOrganization(String responsibleOrganization) { + this.responsibleOrganization = responsibleOrganization; + } + + public String getProjectTitle() { + return projectTitle; + } + + public void setProjectTitle(String projectTitle) { + this.projectTitle = projectTitle; + } + + public java.util.List> getTranslations() { + return translations; + } + + public void setTranslations(java.util.List> translations) { + this.translations = translations; + } + + public String getSecondarySector() { + return secondarySector; + } + + public void setSecondarySector(String secondarySector) { + this.secondarySector = secondarySector; + } +} \ No newline at end of file diff --git a/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java b/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java index a4b0907f7fe..4ff91788e2f 100644 --- a/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java +++ b/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java @@ -5,10 +5,32 @@ import org.dgfoundation.amp.ar.ArConstants; import org.dgfoundation.amp.ar.ColumnConstants; import org.dgfoundation.amp.ar.MeasureConstants; -import org.dgfoundation.amp.newreports.*; +import org.dgfoundation.amp.newreports.AmountCell; +import org.dgfoundation.amp.newreports.FilterRule; +import org.dgfoundation.amp.newreports.GeneratedReport; +import org.dgfoundation.amp.newreports.GroupingCriteria; +import org.dgfoundation.amp.newreports.IntCell; +import org.dgfoundation.amp.newreports.ReportArea; +import org.dgfoundation.amp.newreports.ReportCell; +import org.dgfoundation.amp.newreports.ReportColumn; +import org.dgfoundation.amp.newreports.ReportElement; +import org.dgfoundation.amp.newreports.ReportFiltersImpl; +import org.dgfoundation.amp.newreports.ReportMeasure; +import org.dgfoundation.amp.newreports.ReportOutputColumn; +import org.dgfoundation.amp.newreports.ReportSettingsImpl; +import org.dgfoundation.amp.newreports.ReportSpecificationImpl; +import org.dgfoundation.amp.newreports.ReportsDashboard; +import org.dgfoundation.amp.newreports.TextCell; import org.digijava.kernel.ampapi.endpoints.common.EndpointUtils; +import org.digijava.kernel.entity.Locale; +import org.digijava.kernel.persistence.PersistenceManager; +import org.digijava.kernel.request.Site; +import org.digijava.kernel.request.TLSUtils; +import org.digijava.kernel.translator.TranslatorWorker; import org.digijava.module.aim.helper.GlobalSettingsConstants; import org.digijava.module.aim.util.FeaturesUtil; +import org.hibernate.Session; +import org.hibernate.query.Query; import org.jetbrains.annotations.NotNull; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @@ -16,27 +38,41 @@ import java.io.IOException; import java.io.OutputStream; +import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; public class AmpDonorFundingJob extends ConnectionCleaningJob implements StatefulJob { protected static final Logger logger = Logger.getLogger(AmpDonorFundingJob.class); + private List currencies = new ArrayList<>(java.util.Arrays.asList("USD", "EUR")); + /*private String reportName = "preview report"; + private Integer reportType = ArConstants.DONOR_TYPE;*/ @Override public void executeInternal(JobExecutionContext context) throws JobExecutionException { - List ampDashboardFundingCombinedUSD = getFundingByCurrency("USD"); - List ampDashboardFundingCombinedEUR = getFundingByCurrency("EUR"); - List ampDashboardFundingCombinedXDR = getFundingByCurrency("XDR"); + //TODO make currency configurable + AmpJobsUtil.populateRequest(); + Long ampTeamId = FeaturesUtil + .getGlobalSettingValueLong(GlobalSettingsConstants.WORKSPACE_TO_RUN_REPORT_FROM_JOB); + AmpJobsUtil.setTeamForNonRequestReport(ampTeamId); + List ampDashboardFundingCombined = new ArrayList<>(); + currencies = Objects.requireNonNull(FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DASHBOARD_CURRENCIES)).isEmpty() ? + currencies : + Arrays.asList(Objects.requireNonNull(FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DASHBOARD_CURRENCIES)).split("\\|")); + currencies.forEach(currency -> ampDashboardFundingCombined.addAll(getFundingByCurrency(currency))); + //List ampDashboardFundingCombinedXDR = getFundingByCurrency("XDR"); + + // Get translations for all fields (once, not per record) + List> translations = getTranslations(); String serverUrl = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.AMP_DASHBOARD_URL); - if (serverUrl != null) { - ampDashboardFundingCombinedUSD.addAll(ampDashboardFundingCombinedEUR); - ampDashboardFundingCombinedUSD.addAll(ampDashboardFundingCombinedXDR); - } - sendReportsToServer(ampDashboardFundingCombinedUSD, serverUrl); + assert serverUrl != null; + serverUrl = serverUrl.endsWith("/") ? serverUrl + "amp-funding/importDonorFunding" : serverUrl + "/amp-funding/importDonorFunding"; + sendReportsToServer(ampDashboardFundingCombined, translations, serverUrl); } @NotNull @@ -45,77 +81,166 @@ private List getFundingByCurrency(String currencyCode) { List ampDashboardFunding = processReportData(fundingReport, currencyCode); // The ampDashboardFunding data contains objects for commitments and disbursment differently in // separate objects. We need to combine them in same object combining commitment and disbursment values. - List ampDashboardFundingCombined = ampDashboardFunding.stream() + //Make year configurable + //+ "|" + report.getYear() + return new ArrayList<>(ampDashboardFunding.stream() .collect(Collectors.toMap( - report -> report.getDonorAgency() + "|" + report.getPillar() + "|" + - report.getCountry() + "|" + report.getImplementationLevel() + "|" + - report.getStatus() + "|" + report.getYear() + "|" + report.getReportingSystem() - + "|" + report.getTypeOfAssistance(), + report -> report.getDonorAgency() + + "|" + report.getImplementingAgency() + + "|" + report.getPillar() + + "|" + report.getLocation() + + "|" + report.getImplementationLevel() + + "|" + report.getStatus() + //+ "|" + report.getYear() + + "|" + report.getReportingSystem() + + "|" + report.getTypeOfAssistance() + + "|" + report.getProcurementSystem() + + "|" + report.getResponsibleOrganization() + + "|" + report.getSecondarySector(), report -> report, (report1, report2) -> { report1.sumWith(report2); return report1; })) - .values() - .stream() - .collect(Collectors.toList()); - return ampDashboardFundingCombined; + .values()); } private List processReportData(GeneratedReport report, String currencyCode) { + logger.info("Processing report data for currency: " + currencyCode); + logger.info("Number of leaf headers: " + report.leafHeaders.size()); - ReportOutputColumn donorAgency = report.leafHeaders.get(0); - ReportOutputColumn pilar = report.leafHeaders.get(1); - ReportOutputColumn implementationLevel = report.leafHeaders.get(2); - ReportOutputColumn country = report.leafHeaders.get(3); - ReportOutputColumn status = report.leafHeaders.get(4); - ReportOutputColumn typeOfAssistance = report.leafHeaders.get(5); - ReportOutputColumn reportingSystem = report.leafHeaders.get(6); // Also called Forum - + // Find columns by name for robustness (works regardless of order) + // Note: Indices are based on the order in addColumnsToSpecification: + // Hierarchies: 0-10, then AMP_ID (11), ACTIVITY_COUNT (12), measures (13-14) + ReportOutputColumn donorAgency = findColumnByName(report.leafHeaders, ColumnConstants.DONOR_AGENCY, 0); + ReportOutputColumn implementingAgency = findColumnByName(report.leafHeaders, ColumnConstants.IMPLEMENTING_AGENCY, 1); + ReportOutputColumn procurementSystemAgency = findColumnByName(report.leafHeaders, ColumnConstants.PROCUREMENT_SYSTEM, 2); + ReportOutputColumn pilar = findColumnByName(report.leafHeaders, ColumnConstants.NATIONAL_PLANNING_OBJECTIVES_LEVEL_1, 3); + ReportOutputColumn implementationLevel = findColumnByName(report.leafHeaders, ColumnConstants.IMPLEMENTATION_LEVEL, 4); + String location_adm_level = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DONOR_FUNDING_ADM_LEVEL); + ReportOutputColumn impLocation = findColumnByName(report.leafHeaders, location_adm_level, 5); + ReportOutputColumn status = findColumnByName(report.leafHeaders, ColumnConstants.STATUS, 6); + ReportOutputColumn typeOfAssistance = findColumnByName(report.leafHeaders, ColumnConstants.TYPE_OF_ASSISTANCE, 7); + ReportOutputColumn reportingSystem = findColumnByName(report.leafHeaders, ColumnConstants.PRIMARY_SECTOR, 8); // Also called Forum + ReportOutputColumn responsibleOrg = findColumnByName(report.leafHeaders, ColumnConstants.RESPONSIBLE_ORGANIZATION, 9); + ReportOutputColumn secondarySector = findColumnByName(report.leafHeaders, ColumnConstants.SECONDARY_SECTOR, 10); + ReportOutputColumn ampId = findColumnByName(report.leafHeaders, ColumnConstants.AMP_ID, 11); // AMP_ID is now after hierarchies List ampDashboardFunding = new ArrayList<>(); + for (ReportArea child : report.reportContents.getChildren()) { TextCell donorAgencyCell = (TextCell) child.getContents().get(donorAgency); + if (child.getChildren() != null) { - for (ReportArea pilarData : child.getChildren()) { - TextCell pilarCell = (TextCell) pilarData.getContents().get(pilar); - for (ReportArea implLevel : pilarData.getChildren()) { - TextCell implLevelCell = (TextCell) implLevel.getContents().get(implementationLevel); - for (ReportArea location : implLevel.getChildren()) { - TextCell countryCell = (TextCell) location.getContents().get(country); - for (ReportArea statusData : location.getChildren()) { - TextCell statusCell = (TextCell) statusData.getContents().get(status); - for (ReportArea typeOfAssistanceData : statusData.getChildren()) { - //for (ReportArea reportSystemData : statusData.getChildren()) { - TextCell typeOfAssistanceCell= (TextCell) typeOfAssistanceData.getContents().get(typeOfAssistance); - //TextCell reportSystemCell = (TextCell) reportSystemData.getContents().get(reportingSystem); - for (ReportArea reportSystemData : typeOfAssistanceData.getChildren()) { - //for (ReportArea typeOfAssistanceData : reportSystemData.getChildren()) { - TextCell reportSystemCell = (TextCell) reportSystemData.getContents().get(reportingSystem); - //TextCell typeOfAssistanceCell = (TextCell) typeOfAssistanceData.getContents().get(typeOfAssistance); - for (Map.Entry content : reportSystemData.getContents().entrySet()) { - - - ReportOutputColumn col = content.getKey(); - if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || col.originalColumnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { - if (!col.parentColumn.originalColumnName.equals("Totals")) { - ReportsDashboard fundingReport = new ReportsDashboard(); - fundingReport.setDonorAgency(donorAgencyCell.value.toString()); - fundingReport.setPillar(pilarCell.value.toString()); - fundingReport.setCountry(countryCell.value.toString()); - fundingReport.setImplementationLevel(implLevelCell.value.toString()); - fundingReport.setStatus(statusCell.value.toString()); - fundingReport.setReportingSystem(reportSystemCell.value.toString()); - fundingReport.setTypeOfAssistance(typeOfAssistanceCell.value.toString()); - fundingReport.setYear(col.parentColumn.originalColumnName); - AmountCell amount = (AmountCell) content.getValue(); - if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS)) { - fundingReport.setActualCommitment(amount.extractValue()); - } else { - fundingReport.setActualDisbursement(amount.extractValue()); + for (ReportArea implementingAgencyData : child.getChildren()) { + TextCell implementingAgencyCell = (TextCell) implementingAgencyData.getContents().get(implementingAgency); + + if (implementingAgencyData.getChildren() != null) { + for (ReportArea procurementSystemAgencyData : implementingAgencyData.getChildren()) { + TextCell procurementSystemAgencyCell = (TextCell) procurementSystemAgencyData.getContents().get(procurementSystemAgency); + + if (procurementSystemAgencyData.getChildren() != null) { + for (ReportArea pilarData : procurementSystemAgencyData.getChildren()) { + TextCell pilarCell = (TextCell) pilarData.getContents().get(pilar); + + if (pilarData.getChildren() != null) { + for (ReportArea implLevel : pilarData.getChildren()) { + TextCell implLevelCell = (TextCell) implLevel.getContents().get(implementationLevel); + + if (implLevel.getChildren() != null) { + for (ReportArea location : implLevel.getChildren()) { + TextCell locationCell = (TextCell) location.getContents().get(impLocation); + + if (location.getChildren() != null) { + for (ReportArea statusData : location.getChildren()) { + TextCell statusCell = (TextCell) statusData.getContents().get(status); + + if (statusData.getChildren() != null) { + for (ReportArea typeOfAssistanceData : statusData.getChildren()) { + TextCell typeOfAssistanceCell = (TextCell) typeOfAssistanceData.getContents().get(typeOfAssistance); + + if (typeOfAssistanceData.getChildren() != null) { + for (ReportArea primarySectorData : typeOfAssistanceData.getChildren()) { + TextCell reportingSystemCell = (TextCell) primarySectorData.getContents().get(reportingSystem); + + if (primarySectorData.getChildren() != null) { + for (ReportArea responsibleOrgData : primarySectorData.getChildren()) { + TextCell responsibleOrgCell = (TextCell) responsibleOrgData.getContents().get(responsibleOrg); + + if (responsibleOrgData.getChildren() != null) { + for (ReportArea secondarySectorData : responsibleOrgData.getChildren()) { + TextCell secondarySectorCell = (TextCell) secondarySectorData.getContents().get(secondarySector); + + Long activityCount = 0L; + // collect activity count + for (Map.Entry content : responsibleOrgData.getContents().entrySet()) { + ReportOutputColumn col = content.getKey(); + if (col.originalColumnName.equals(ColumnConstants.ACTIVITY_COUNT)) { + IntCell amount = (IntCell) content.getValue(); + if (amount != null && amount.value != null) { + activityCount = (Long) amount.value; + } + } + } + // gather AMP IDs from children (leaf nodes) + List ampIdsList = new ArrayList<>(); + if (responsibleOrgData.getChildren() != null) { + for (ReportArea ampIdData : responsibleOrgData.getChildren()) { + TextCell ampIdCell = (TextCell) ampIdData.getContents().get(ampId); + if (ampIdCell != null && ampIdCell.value != null) { + ampIdsList.add(ampIdCell.value.toString()); + } + } + } + // fallback if AMP ID directly on this node and no children + if (ampIdsList.isEmpty()) { + TextCell directAmpIdCell = (TextCell) responsibleOrgData.getContents().get(ampId); + if (directAmpIdCell != null && directAmpIdCell.value != null) { + ampIdsList.add(directAmpIdCell.value.toString()); + } + } + String ampIdsJoined = String.join(",", ampIdsList); + + // now process measures + for (Map.Entry content : responsibleOrgData.getContents().entrySet()) { + ReportOutputColumn col = content.getKey(); + if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || col.originalColumnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { + if (col.parentColumn != null && col.parentColumn.originalColumnName.equals("Totals")) { + ReportsDashboard fundingReport = new ReportsDashboard(); + fundingReport.setDonorAgency(donorAgencyCell != null ? donorAgencyCell.value.toString() : null); + fundingReport.setImplementingAgency(implementingAgencyCell != null ? implementingAgencyCell.value.toString() : null); + fundingReport.setPillar(pilarCell != null ? pilarCell.value.toString() : null); + fundingReport.setLocation(locationCell != null ? locationCell.value.toString() : null); + fundingReport.setImplementationLevel(implLevelCell != null ? implLevelCell.value.toString() : null); + fundingReport.setStatus(statusCell != null ? statusCell.value.toString() : null); + fundingReport.setReportingSystem(reportingSystemCell != null ? reportingSystemCell.value.toString() : null); + fundingReport.setTypeOfAssistance(typeOfAssistanceCell != null ? typeOfAssistanceCell.value.toString() : null); + fundingReport.setProcurementSystem(procurementSystemAgencyCell != null ? procurementSystemAgencyCell.value.toString() : null); + fundingReport.setResponsibleOrganization(responsibleOrgCell != null ? responsibleOrgCell.value.toString() : null); + fundingReport.setSecondarySector(secondarySectorCell != null ? secondarySectorCell.value.toString() : null); + fundingReport.setActivityCount(activityCount); + fundingReport.setCurrency(currencyCode); + fundingReport.setActivityIds(ampIdsJoined); + AmountCell amount = (AmountCell) content.getValue(); + if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS)) { + fundingReport.setActualCommitment(amount.extractValue()); + } else { + fundingReport.setActualDisbursement(amount.extractValue()); + } + ampDashboardFunding.add(fundingReport); + } + } + } + } + } + } + } + } + } + } + } + } } - fundingReport.setCurrency(currencyCode); - ampDashboardFunding.add(fundingReport); } } } @@ -130,6 +255,64 @@ private List processReportData(GeneratedReport report, String return ampDashboardFunding; } + /** + * Find a column by name in leaf headers, with fallback to index if name doesn't match + * This provides robustness in case the order changes + */ + private ReportOutputColumn findColumnByName(List leafHeaders, String columnName, int expectedIndex) { + if (expectedIndex < leafHeaders.size()) { + ReportOutputColumn col = leafHeaders.get(expectedIndex); + // Verify the column name matches (case-insensitive comparison of originalColumnName) + if (col.originalColumnName != null && col.originalColumnName.equalsIgnoreCase(columnName)) { + return col; + } + if (columnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || columnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { + assert col.originalColumnName != null; + if (col.parentColumn.columnName.equalsIgnoreCase(columnName)) { + logger.info("Found Measure" + columnName + " at expected index " + expectedIndex); + return col; + } + } + + + // If index doesn't match, search by name + logger.warn("Column at index " + expectedIndex + " is " + col.originalColumnName + + ", expected " + columnName + ". Searching by name..."); + } + + // Fallback: search by name (works for both columns and measures) + for (ReportOutputColumn col : leafHeaders) { + if (col.originalColumnName != null && col.originalColumnName.equalsIgnoreCase(columnName)) { + logger.info("Found " + columnName + " at different position"); + return col; + } + if (columnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || columnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { + if (col.parentColumn != null && col.parentColumn.columnName.equalsIgnoreCase(columnName)) { + logger.info("Found Measure" + columnName + " at different position"); + return col; + } + } + } + + // For measures, also check if they're nested under a parent column (like "Totals") + // This handles cases where measures might be structured differently +// if (columnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || columnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { +// for (ReportOutputColumn col : leafHeaders) { +// // Check if parent column exists and is "Totals", and this column's name matches the measure +// if (col.parentColumn != null && col.parentColumn.originalColumnName != null +// && ("Totals".equalsIgnoreCase(col.parentColumn.originalColumnName) +// || "TOTALS".equals(col.parentColumn.originalColumnName)) +// && col.originalColumnName != null && col.originalColumnName.equalsIgnoreCase(columnName)) { +// logger.info("Found measure column " + columnName + " under Totals"); +// return col; +// } +// } +// } + + throw new RuntimeException("Column not found: " + columnName + " in leaf headers. Available columns: " + + leafHeaders.stream().map(c -> c.originalColumnName != null ? c.originalColumnName : "null").collect(Collectors.joining(", "))); + } + private void addFilters(ReportSpecificationImpl spec) { if (spec.getFilters() == null) { spec.setFilters(new ReportFiltersImpl()); @@ -150,43 +333,164 @@ private GeneratedReport generateReport(String currencyCode) { ReportSpecificationImpl spec = new ReportSpecificationImpl("preview report", ArConstants.DONOR_TYPE); addColumnsToSpecification(spec); - spec.setSummaryReport(true); - spec.setGroupingCriteria(GroupingCriteria.GROUPING_YEARLY); + spec.setSummaryReport(false); + //TODO broken by year configurable + spec.setGroupingCriteria(GroupingCriteria.GROUPING_TOTALS_ONLY); spec.setShowOriginalCurrency(false); spec.setDisplayEmptyFundingRows(true); ReportSettingsImpl reportSettings = new ReportSettingsImpl(); spec.setSettings(reportSettings); - addFilters(spec); + //addFilters(spec); reportSettings.setCurrencyCode(currencyCode); return EndpointUtils.runReport(spec); } private void addColumnsToSpecification(ReportSpecificationImpl spec) { + String location_adm_level = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DONOR_FUNDING_ADM_LEVEL); + // Add hierarchy columns first spec.addColumn(new ReportColumn(ColumnConstants.DONOR_AGENCY)); + spec.addColumn(new ReportColumn(ColumnConstants.IMPLEMENTING_AGENCY)); + spec.addColumn(new ReportColumn(ColumnConstants.PROCUREMENT_SYSTEM)); spec.addColumn(new ReportColumn(ColumnConstants.NATIONAL_PLANNING_OBJECTIVES_LEVEL_1)); spec.addColumn(new ReportColumn(ColumnConstants.IMPLEMENTATION_LEVEL)); - spec.addColumn(new ReportColumn(ColumnConstants.LOCATION_ADM_LEVEL_0)); + spec.addColumn(new ReportColumn(location_adm_level)); spec.addColumn(new ReportColumn(ColumnConstants.STATUS)); spec.addColumn(new ReportColumn(ColumnConstants.TYPE_OF_ASSISTANCE)); - spec.addColumn(new ReportColumn(ColumnConstants.REPORTING_SYSTEM)); + //TODO for GGW this is reporting system, for others it is Sectors + spec.addColumn(new ReportColumn(ColumnConstants.PRIMARY_SECTOR)); + spec.addColumn(new ReportColumn(ColumnConstants.RESPONSIBLE_ORGANIZATION)); + spec.addColumn(new ReportColumn(ColumnConstants.SECONDARY_SECTOR)); + // Ensure AMP ID is part of the hierarchy so commitments roll up under each activity + + // Set hierarchies - includes AMP_ID so commitments/disbursements roll up under each activity + // This is required for summary reports to include all columns in leaf headers + Set hierarchyColumns = new LinkedHashSet<>(spec.getColumns()); + spec.setHierarchies(hierarchyColumns); - spec.setHierarchies(spec.getColumns()); + // Add non-hierarchy columns after setHierarchies + spec.addColumn(new ReportColumn(ColumnConstants.AMP_ID)); + spec.addColumn(new ReportColumn(ColumnConstants.ACTIVITY_COUNT)); spec.addMeasure(new ReportMeasure(MeasureConstants.ACTUAL_COMMITMENTS)); spec.addMeasure(new ReportMeasure(MeasureConstants.ACTUAL_DISBURSEMENTS)); + + logger.info("Report columns set for Donor Funding Report" + spec.getColumns().size()); } - public static void sendReportsToServer(List ampDashboardFunding, String serverUrl) { + /** + * Gets translations for all field labels (returns once, not per record) + */ + private List> getTranslations() { + List> translations = new ArrayList<>(); + + try { + // Get all available locales + List localeCodes = getAvailableLocaleCodes(); + if (localeCodes.isEmpty()) { + logger.warn("No available locales found, skipping translations"); + return translations; + } + + // Get the site + Site site = TLSUtils.getSite(); + if (site == null) { + logger.warn("No site found, skipping translations"); + return translations; + } + + // Get location column name + String locationAdmLevel = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DONOR_FUNDING_ADM_LEVEL); + + // Define field mappings: field name -> column constant + Map fieldMappings = new HashMap<>(); + fieldMappings.put("donorAgency", ColumnConstants.DONOR_AGENCY); + fieldMappings.put("implementingAgency", ColumnConstants.IMPLEMENTING_AGENCY); + fieldMappings.put("pillar", ColumnConstants.NATIONAL_PLANNING_OBJECTIVES_LEVEL_1); + fieldMappings.put("location", locationAdmLevel != null ? locationAdmLevel : ColumnConstants.LOCATION); + fieldMappings.put("implementationLevel", ColumnConstants.IMPLEMENTATION_LEVEL); + fieldMappings.put("status", ColumnConstants.STATUS); + fieldMappings.put("reportingSystem", ColumnConstants.PRIMARY_SECTOR); + fieldMappings.put("typeOfAssistance", ColumnConstants.TYPE_OF_ASSISTANCE); + fieldMappings.put("procurementSystem", ColumnConstants.PROCUREMENT_SYSTEM); + fieldMappings.put("responsibleOrganization", ColumnConstants.RESPONSIBLE_ORGANIZATION); + fieldMappings.put("currency", "Currency"); + fieldMappings.put("actualCommitment", MeasureConstants.ACTUAL_COMMITMENTS); + fieldMappings.put("actualDisbursement", MeasureConstants.ACTUAL_DISBURSEMENTS); + fieldMappings.put("activityCount", ColumnConstants.ACTIVITY_COUNT); + fieldMappings.put("activityIds", "Activity IDs"); + fieldMappings.put("projectTitle", "Project Title"); + + // Generate translations for all fields once + for (Map.Entry entry : fieldMappings.entrySet()) { + String fieldName = entry.getKey(); + String label = entry.getValue(); + Map fieldTranslation = getFieldTranslations(label, localeCodes, site); + fieldTranslation.put("label", fieldName); + translations.add(fieldTranslation); + } + + logger.info("Generated translations for " + translations.size() + " fields with " + localeCodes.size() + " locales"); + } catch (Exception e) { + logger.error("Error getting translations", e); + } + + return translations; + } + + /** + * Gets all available locale codes from the database + */ + private List getAvailableLocaleCodes() { + List localeCodes = new ArrayList<>(); + Session session = null; + try { + session = PersistenceManager.getSession(); + Query query = session.createQuery("SELECT l.code FROM " + Locale.class.getName() + " l WHERE l.available = true", String.class); + List codes = query.list(); + localeCodes.addAll(codes); + } catch (Exception e) { + logger.error("Error getting available locales", e); + } + return localeCodes; + } + + /** + * Gets translations for a field label in all available locales + */ + private Map getFieldTranslations(String label, List localeCodes, Site site) { + Map translations = new HashMap<>(); + for (String localeCode : localeCodes) { + try { + String translated = TranslatorWorker.translateText(label, localeCode, site); + translations.put(localeCode, translated != null ? translated : label); + } catch (Exception e) { + logger.warn("Error translating label '" + label + "' for locale '" + localeCode + "': " + e.getMessage()); + translations.put(localeCode, label); + } + } + return translations; + } + + public static void sendReportsToServer(List ampDashboardFunding, List> translations, String serverUrl) { try { // Create a URL object with the server's endpoint URL + logger.info("Sending data to amp dashboard at: " + serverUrl); + logger.info("Number of records to send: " + ampDashboardFunding.size()); HttpURLConnection connection = getHttpURLConnection(serverUrl); - // Convert the ampDashboardFunding to JSON using a JSON library (e.g., Gson) + + // Create a wrapper object with reports and translations + Map submissionData = new HashMap<>(); + submissionData.put("reports", ampDashboardFunding); + submissionData.put("translations", translations); + + // Convert to JSON using a JSON library (e.g., Gson) Gson gson = new Gson(); - String jsonData = gson.toJson(ampDashboardFunding); + String jsonData = gson.toJson(submissionData); +// logger.info("JSON data: " + jsonData); // Get the output stream of the connection try (OutputStream os = connection.getOutputStream()) { // Write the JSON data to the output stream - os.write(jsonData.getBytes("UTF-8")); + os.write(jsonData.getBytes(StandardCharsets.UTF_8)); } // Get the HTTP response code @@ -195,10 +499,10 @@ public static void sendReportsToServer(List ampDashboardFundin // Check if the request was successful (e.g., HTTP 200 OK) if (responseCode == 200) { // The data has been successfully sent to the server - logger.debug("Data sent successfully to amp dashboard. HTTP Response Code: " + responseCode); + logger.info("Data sent successfully to amp dashboard. HTTP Response Code: " + responseCode); } else { // Handle the error condition (e.g., log an error message) - logger.debug("Error sending data to amp dashboard. HTTP Response Code: " + responseCode); + logger.info("Error sending data to amp dashboard. HTTP Response Code: " + responseCode); } // Close the connection @@ -225,4 +529,4 @@ private static HttpURLConnection getHttpURLConnection(String serverUrl) throws I connection.setDoOutput(true); return connection; } -} +} \ No newline at end of file From 5d998ec53df3367da87f07c988994ae6fbbc02f8 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Mon, 27 Apr 2026 09:28:16 +0300 Subject: [PATCH 02/20] Add workflow to old codebase --- .github/workflows/deploy.yml | 708 +++++++++++++++++++++++++++++++++++ 1 file changed, 708 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000000..38f13835bcc --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,708 @@ +name: Deploy AMP + +on: + workflow_dispatch: + inputs: + environment: + description: 'Server to deploy' + required: true + type: choice + options: + - staging + - de + country: + description: 'Country to deploy (will be validated against available countries from server)' + required: true + type: choice + options: + - bfaso + - boad + - chad + - civ + - drc + - ecowas + - egypt + - ethiopia + - gambia + - ggw + - haiti + - haitiiati + - haititraining + - honduras + - honduraslight + - honduraslightssc + - jordan + - kosovo + - kyrgyzstan + - liberia + - madagascar + - malawi + - moldova + - nepal + - niger + - rdidemo + - rwanda + - rwandatest + - senegal + - senegalgiz + - tanzania + - timor + - togo + - uganda + - xchad + pr_number: + description: 'PR number (optional - if provided, will use pr-{number} format for tag and URL). See workflow logs for available PRs.' + required: false + type: string + +env: + PG_VERSION: 14 + # Reference list from GitHub Repository Variables + COMMON_COUNTRIES: ${{ vars.COMMON_COUNTRIES }} + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + timeout-minutes: 60 # Add timeout to prevent hanging jobs + steps: + - name: Setup SSH agent with build and deploy keys + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.DOCKER_BUILD_SSH_KEY }} + ${{ secrets.DEPLOY_SSH_PRIVATE_KEY }} + + - name: Configure git to use SSH for submodules + run: | + # Configure git to rewrite HTTPS URLs to SSH for submodules + git config --global url."git@github.com:".insteadOf "https://github.com/" + # Add GitHub to known hosts + mkdir -p ~/.ssh + ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2>/dev/null || true + + - name: Checkout code with submodules (default branch) + if: inputs.pr_number == '' + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: true + # Use SSH for private submodules + ssh-key: ${{ secrets.DOCKER_BUILD_SSH_KEY || '' }} + + - name: List available PRs and validate + if: inputs.pr_number != '' + id: pr_info + run: | + set -e + echo "📋 Fetching list of open pull requests..." + echo "" + + # Fetch open PRs using GitHub API + REPO="${{ github.repository }}" + PR_NUMBER="${{ inputs.pr_number }}" + + # Validate PR number is numeric + if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then + echo "❌ Error: PR number must be numeric, got: ${PR_NUMBER}" + exit 1 + fi + + # Get list of open PRs (limit to 50 most recent) + PRS_RESPONSE=$(curl -s -H "Authorization: token ${{ github.token }}" \ + "https://api.github.com/repos/${REPO}/pulls?state=open&per_page=50&sort=updated&direction=desc") + + if [ -z "$PRS_RESPONSE" ] || [ "$PRS_RESPONSE" == "[]" ]; then + echo "❌ Error: Could not fetch PR list or no open PRs found" + echo " Cannot validate PR #${PR_NUMBER}" + exit 1 + fi + + echo "Available open PRs:" + echo "$PRS_RESPONSE" | jq -r '.[] | " #\(.number) - \(.title) (branch: \(.head.ref))"' || echo "Could not parse PR list" + echo "" + + # Validate the provided PR number exists in open PRs + PR_EXISTS=$(echo "$PRS_RESPONSE" | jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .number' || echo "") + + if [ -z "$PR_EXISTS" ]; then + echo "❌ Error: PR #${PR_NUMBER} not found in open PRs list" + echo "" + echo "The PR might be:" + echo " - Closed or merged" + echo " - Not yet created" + echo " - Incorrect number" + echo "" + echo "Please verify the PR number and try again." + exit 1 + fi + + # Get PR details + PR_TITLE=$(echo "$PRS_RESPONSE" | jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .title' || echo "") + PR_BRANCH=$(echo "$PRS_RESPONSE" | jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .head.ref' || echo "") + PR_HEAD_REPO=$(echo "$PRS_RESPONSE" | jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .head.repo.full_name' || echo "") + PR_HEAD_SHA=$(echo "$PRS_RESPONSE" | jq -r --arg pr "$PR_NUMBER" '.[] | select(.number == ($pr | tonumber)) | .head.sha' || echo "") + + echo "✅ PR #${PR_NUMBER} found: ${PR_TITLE}" + echo " Branch: ${PR_BRANCH}" + echo " Repository: ${PR_HEAD_REPO}" + echo " SHA: ${PR_HEAD_SHA}" + + # Store PR branch info for checkout step + echo "PR_BRANCH=${PR_BRANCH}" >> $GITHUB_OUTPUT + echo "PR_HEAD_REPO=${PR_HEAD_REPO}" >> $GITHUB_OUTPUT + echo "PR_HEAD_SHA=${PR_HEAD_SHA}" >> $GITHUB_OUTPUT + + - name: Checkout PR branch + if: inputs.pr_number != '' + uses: actions/checkout@v4 + with: + ref: ${{ steps.pr_info.outputs.PR_BRANCH }} + repository: ${{ steps.pr_info.outputs.PR_HEAD_REPO }} + fetch-depth: 0 + submodules: true + # Use SSH for private submodules + ssh-key: ${{ secrets.DOCKER_BUILD_SSH_KEY || '' }} + + - name: Set up JDK for Maven + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + # Note: Maven cache is not needed here since Maven runs inside Docker + # Docker build uses its own Maven cache via --mount=type=cache,target=/root/.m2 + + - name: Read AMP version from pom.xml + id: amp_version + run: | + # Parse version directly from pom.xml (much faster than running Maven) + # Try to get project.version property first, then fallback to version tag + # This avoids Maven initialization which can take 30+ seconds + + # Method 1: Try to extract project.version property using sed + VERSION=$(sed -n 's/.*\([^<]*\)<\/project\.version>.*/\1/p' amp/pom.xml | head -1) + + # Method 2: If not found, extract from version tag and remove -SNAPSHOT + if [ -z "$VERSION" ]; then + VERSION=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' amp/pom.xml | head -1 | sed 's/-SNAPSHOT//') + fi + + # Clean up: trim whitespace + VERSION=$(echo "$VERSION" | xargs) + + # Fallback to default if still empty + if [ -z "$VERSION" ]; then + echo "⚠️ Could not parse version from amp/pom.xml, using default 4.0" + VERSION="4.0" + fi + + echo "AMP_VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "AMP Version: $VERSION (parsed from amp/pom.xml in <1s vs 30s+ for Maven)" + + - name: Generate deployment tag + id: tag + run: | + # Check if PR number is provided + if [ -n "${{ inputs.pr_number }}" ]; then + PR_NUMBER="${{ inputs.pr_number }}" + TAG="pr-${PR_NUMBER}" + echo "PR_NUMBER=${PR_NUMBER}" >> $GITHUB_OUTPUT + echo "Deployment tag: $TAG (PR #${PR_NUMBER})" + else + # Handle branch-based deployments + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + if [[ "$BRANCH_NAME" =~ ^feature/AMP-[0-9]+.* ]]; then + JIRA_ID=$(echo "$BRANCH_NAME" | sed -n 's/^feature\/AMP-\([0-9]\+\).*/\1/p') + TAG="feature-${JIRA_ID}" + else + TAG=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9_-]/-/g' | tr '[:upper:]' '[:lower:]') + fi + echo "Deployment tag: $TAG" + fi + echo "TAG=$TAG" >> $GITHUB_OUTPUT + + - name: Validate AWS credentials + run: | + if [ -z "${{ secrets.AWS_ACCESS_KEY_ID }}" ]; then + echo "❌ Error: AWS_ACCESS_KEY_ID secret is not set" + exit 1 + fi + if [ -z "${{ secrets.AWS_SECRET_ACCESS_KEY }}" ]; then + echo "❌ Error: AWS_SECRET_ACCESS_KEY secret is not set" + exit 1 + fi + if [ -z "${{ secrets.ECR_REGISTRY }}" ]; then + echo "❌ Error: ECR_REGISTRY secret is not set" + exit 1 + fi + echo "✅ AWS credentials and ECR registry are configured" + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Verify AWS credentials + run: | + echo "Verifying AWS credentials..." + aws sts get-caller-identity || { + echo "❌ Error: Failed to authenticate with AWS" + echo "Please verify that AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are correct" + exit 1 + } + echo "✅ AWS credentials verified" + + - name: Get ECR login token + id: ecr-login + run: | + set +x # Avoid command echoing to prevent token exposure + echo "Getting ECR login token for ${{ secrets.ECR_REGISTRY }}..." + PASSWORD=$(aws ecr get-login-password --region us-east-1) || { + echo "❌ Error: Failed to get ECR login token" + echo "Please verify:" + echo " 1. AWS credentials have ECR permissions" + echo " 2. ECR registry exists: ${{ secrets.ECR_REGISTRY }}" + echo " 3. Region is correct: us-east-1" + exit 1 + } + # Use ::add-mask:: to prevent token from appearing in logs + echo "::add-mask::$PASSWORD" + echo "password=$PASSWORD" >> $GITHUB_OUTPUT + echo "✅ ECR login token obtained" + + - name: Login to Container Registry (ECR) + run: | + set +x # Avoid command echoing to prevent token exposure + echo "${{ steps.ecr-login.outputs.password }}" | docker login --username AWS --password-stdin ${{ secrets.ECR_REGISTRY }} 2>&1 | sed 's/.*password.*/***/g' + echo "✅ Successfully logged in to ECR" + + - name: Set deployment hostname and user + id: deploy_config + run: | + # Use inputs from workflow_dispatch + ENV="${{ inputs.environment }}" + COUNTRY="${{ inputs.country }}" + + # Store environment and country + echo "ENV=${ENV}" >> $GITHUB_OUTPUT + echo "COUNTRY=${COUNTRY}" >> $GITHUB_OUTPUT + + if [[ "$ENV" == "de" ]]; then + echo "DEPLOY_HOST=${{ vars.AMP_DE_HOSTNAME }}" >> $GITHUB_OUTPUT + # For PRs, use pr-{number} format in URL + if [ -n "${{ inputs.pr_number }}" ]; then + echo "AMP_URL=http://amp-${COUNTRY}-pr-${{ inputs.pr_number }}.de.ampsite.net/" >> $GITHUB_OUTPUT + else + echo "AMP_URL=http://amp-${COUNTRY}-${{ steps.tag.outputs.TAG }}.de.ampsite.net/" >> $GITHUB_OUTPUT + fi + else + echo "DEPLOY_HOST=${{ vars.AMP_STAGING_HOSTNAME }}" >> $GITHUB_OUTPUT + # For PRs, use pr-{number} format in URL + if [ -n "${{ inputs.pr_number }}" ]; then + echo "AMP_URL=http://amp-${COUNTRY}-pr-${{ inputs.pr_number }}.stg.ampsite.net/" >> $GITHUB_OUTPUT + else + echo "AMP_URL=http://amp-${COUNTRY}-${{ steps.tag.outputs.TAG }}.stg.ampsite.net/" >> $GITHUB_OUTPUT + fi + fi + + # Store PR number if provided + if [ -n "${{ inputs.pr_number }}" ]; then + echo "PR_NUMBER=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT + fi + + # Set deploy user from vars with fallback to 'jenkins' + if [ -n "${{ vars.DEPLOY_USER }}" ]; then + echo "DEPLOY_USER=${{ vars.DEPLOY_USER }}" >> $GITHUB_OUTPUT + else + echo "DEPLOY_USER=jenkins" >> $GITHUB_OUTPUT + fi + + - name: Setup SSH config for bastion + run: | + mkdir -p ~/.ssh + chmod 700 ~/.ssh + + DEPLOY_HOST="${{ steps.deploy_config.outputs.DEPLOY_HOST }}" + DEPLOY_USER="${{ steps.deploy_config.outputs.DEPLOY_USER }}" + BASTION_HOST="${{ secrets.BASTION_HOST }}" + BASTION_USER="${{ vars.BASTION_USER }}" + + # Use default bastion user if not specified + if [ -z "$BASTION_USER" ] || [ "$BASTION_USER" = "" ]; then + BASTION_USER="jenkins" + fi + + # Create SSH config matching local configuration + # Pattern: *.aws uses ProxyCommand ssh -W %h.devgateway.org:%p bastion + if [ -n "$BASTION_HOST" ] && [ "$BASTION_HOST" != "" ]; then + # Check if deployment host matches *.aws pattern for ProxyCommand + if echo "$DEPLOY_HOST" | grep -q "\.aws"; then + # Use ProxyCommand pattern for *.aws hosts (matching local SSH config) + # Pattern: %h.devgateway.org means if host is "ampdev.aws", target is "ampdev.aws.devgateway.org" + if echo "$DEPLOY_HOST" | grep -q "\.aws\.devgateway\.org$"; then + # Already in full format: something.aws.devgateway.org + TARGET_HOST="$DEPLOY_HOST" + elif echo "$DEPLOY_HOST" | grep -q "\.aws$"; then + # Transform: something.aws -> something.aws.devgateway.org (matching %h.devgateway.org pattern) + TARGET_HOST="${DEPLOY_HOST}.devgateway.org" + else + # Fallback: use as-is + TARGET_HOST="$DEPLOY_HOST" + fi + + cat >> ~/.ssh/config << EOF + Host bastion + HostName $BASTION_HOST + User $BASTION_USER + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ControlMaster no + + Host $DEPLOY_HOST + HostName $DEPLOY_HOST + User $DEPLOY_USER + ProxyCommand ssh -W $TARGET_HOST:%p -o ClearAllForwardings=no bastion + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ControlMaster no + EOF + else + # Use ProxyJump for non-*.aws hosts + cat >> ~/.ssh/config << EOF + Host bastion + HostName $BASTION_HOST + User $BASTION_USER + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ControlMaster no + + Host $DEPLOY_HOST + HostName $DEPLOY_HOST + User $DEPLOY_USER + ProxyJump bastion + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ControlMaster no + EOF + fi + else + # No bastion - direct connection + cat >> ~/.ssh/config << EOF + Host $DEPLOY_HOST + HostName $DEPLOY_HOST + User $DEPLOY_USER + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ControlMaster no + EOF + fi + + chmod 600 ~/.ssh/config + echo "✅ Configured SSH to use bastion: ${BASTION_HOST:-'none (direct connection)'}" + echo "✅ Using ProxyCommand pattern matching your local config" + echo "✅ Bastion user: $BASTION_USER" + echo "✅ Deployment host: $DEPLOY_HOST" + echo "" + echo "SSH configuration:" + cat ~/.ssh/config + + - name: Test SSH connection + run: | + DEPLOY_HOST="${{ steps.deploy_config.outputs.DEPLOY_HOST }}" + DEPLOY_USER="${{ steps.deploy_config.outputs.DEPLOY_USER }}" + echo "Testing SSH connection through bastion..." + ssh $DEPLOY_USER@$DEPLOY_HOST "echo 'SSH connection successful'" + + - name: Get available countries list + id: countries_list + run: | + DEPLOY_HOST="${{ steps.deploy_config.outputs.DEPLOY_HOST }}" + DEPLOY_USER="${{ steps.deploy_config.outputs.DEPLOY_USER }}" + # Get available countries for this version + COUNTRIES=$(ssh $DEPLOY_USER@$DEPLOY_HOST "cd /opt/amp_dbs && amp-db ls ${{ steps.amp_version.outputs.AMP_VERSION }} | sort" || echo "") + COUNTRIES=$(echo "$COUNTRIES" | tr '\n' ' ' | xargs) # Trim whitespace + + if [ -z "$COUNTRIES" ] || [ "$COUNTRIES" = "" ]; then + echo "❌ No database backups compatible with version ${{ steps.amp_version.outputs.AMP_VERSION }}" + exit 1 + fi + + # Store countries list for later use + echo "COUNTRIES<> $GITHUB_OUTPUT + echo "$COUNTRIES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "==========================================" + echo "Available countries for version ${{ steps.amp_version.outputs.AMP_VERSION }}:" + echo "==========================================" + echo "$COUNTRIES" | tr ' ' '\n' + echo "==========================================" + + - name: Validate selected country + run: | + # Get country from deploy_config output + COUNTRY="${{ steps.deploy_config.outputs.COUNTRY }}" + + # Convert countries list to newline-separated for validation + COUNTRIES=$(echo "${{ steps.countries_list.outputs.COUNTRIES }}" | tr ' ' '\n') + + # Check if selected country is available + if ! echo "$COUNTRIES" | grep -q "^${COUNTRY}$"; then + echo "❌ Country '${COUNTRY}' not found in available countries" + echo "" + echo "Available countries:" + echo "$COUNTRIES" + exit 1 + fi + + echo "✅ Country '${COUNTRY}' is available" + + - name: Get database version + id: db_version + run: | + DEPLOY_HOST="${{ steps.deploy_config.outputs.DEPLOY_HOST }}" + DEPLOY_USER="${{ steps.deploy_config.outputs.DEPLOY_USER }}" + # Get country from deploy_config output + COUNTRY="${{ steps.deploy_config.outputs.COUNTRY }}" + DB_VERSION=$(ssh $DEPLOY_USER@$DEPLOY_HOST "cd /opt/amp_dbs && amp-db find ${{ steps.amp_version.outputs.AMP_VERSION }} ${COUNTRY}") + echo "DB_VERSION=$DB_VERSION" >> $GITHUB_OUTPUT + echo "Database version: $DB_VERSION" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:latest + network=host + buildkitd-flags: --debug + + - name: Get commit hash + id: commit_hash + run: | + if git log --pretty=%an -n 1 | grep -q "GitHub Actions"; then + REF="HEAD~1" + else + REF="HEAD" + fi + HASH=$(git rev-parse $REF) + echo "COMMIT_HASH=$HASH" >> $GITHUB_OUTPUT + echo "Commit hash: $HASH" + + + - name: Check if image with commit hash already exists + id: check_image + env: + DOCKER_BUILDKIT: 1 + run: | + COMMIT_HASH="${{ steps.commit_hash.outputs.COMMIT_HASH }}" + # Create a commit-hash-based image tag for content-based lookup + IMAGE_BY_HASH="${{ secrets.ECR_REGISTRY }}/amp/webapp:commit-${COMMIT_HASH:0:12}" + DEPLOY_TAG="${{ steps.tag.outputs.TAG }}" + IMAGE_BY_TAG="${{ secrets.ECR_REGISTRY }}/amp/webapp:${DEPLOY_TAG}" + + echo "Checking for existing image with commit hash ${COMMIT_HASH:0:12}..." + + # Try to pull image by commit hash + if docker pull "$IMAGE_BY_HASH" 2>/dev/null; then + echo "✅ Found existing image for commit ${COMMIT_HASH:0:12}" + echo "SKIP_BUILD=true" >> $GITHUB_OUTPUT + echo "EXISTING_IMAGE=$IMAGE_BY_HASH" >> $GITHUB_OUTPUT + # Tag it with the deployment tag for consistency + docker tag "$IMAGE_BY_HASH" "$IMAGE_BY_TAG" + echo "IMAGE=$IMAGE_BY_TAG" >> $GITHUB_ENV + else + echo "ℹ️ No existing image found for commit ${COMMIT_HASH:0:12}, will build new image" + echo "SKIP_BUILD=false" >> $GITHUB_OUTPUT + echo "IMAGE=$IMAGE_BY_TAG" >> $GITHUB_ENV + fi + + - name: Debug SSH agent + if: steps.check_image.outputs.SKIP_BUILD == 'false' + run: | + echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" + ssh-add -l || echo "No keys in agent" + ssh -o StrictHostKeyChecking=no -T git@github.com 2>&1 || true + + - name: Build Docker image + if: steps.check_image.outputs.SKIP_BUILD == 'false' + env: + DOCKER_BUILDKIT: 1 + BUILDKIT_PROGRESS: plain + run: | + COMMIT_HASH="${{ steps.commit_hash.outputs.COMMIT_HASH }}" + IMAGE_BY_HASH="${{ secrets.ECR_REGISTRY }}/amp/webapp:commit-${COMMIT_HASH:0:12}" + IMAGE_BY_TAG="${{ secrets.ECR_REGISTRY }}/amp/webapp:${{ steps.tag.outputs.TAG }}" + + # Build SSH args - only add if SSH_AUTH_SOCK is available (from ssh-agent) + SSH_ARGS="" + if [ -n "$SSH_AUTH_SOCK" ]; then + SSH_ARGS="--ssh default=$SSH_AUTH_SOCK" + fi + + # Collect cache sources for better layer reuse + CACHE_FROM_ARGS=() + + # Try to use commit-hash-based image as cache (if it exists from a previous build) + if docker pull "$IMAGE_BY_HASH" 2>/dev/null; then + echo "✅ Using commit-hash-based image as cache" + CACHE_FROM_ARGS+=("--cache-from" "$IMAGE_BY_HASH") + fi + + # Try to use deployment tag image as cache + if docker pull "$IMAGE_BY_TAG" 2>/dev/null; then + echo "✅ Using deployment tag image as cache" + CACHE_FROM_ARGS+=("--cache-from" "$IMAGE_BY_TAG") + fi + + # Try to use a recent 'latest' or 'main' branch image as cache (if exists) + LATEST_IMAGE="${{ secrets.ECR_REGISTRY }}/amp/webapp:latest" + if docker pull "$LATEST_IMAGE" 2>/dev/null; then + echo "✅ Using latest image as additional cache source" + CACHE_FROM_ARGS+=("--cache-from" "$LATEST_IMAGE") + fi + + if [ ${#CACHE_FROM_ARGS[@]} -eq 0 ]; then + echo "ℹ️ No existing image found, building from scratch" + fi + + # Build the image with both tags using cache modes for better optimization + # SKIP_TESTS=true to skip Maven and npm tests for faster deployment builds + docker buildx build \ + --progress=plain \ + $SSH_ARGS \ + "${CACHE_FROM_ARGS[@]}" \ + --cache-to type=inline \ + --cache-from type=registry,ref="$IMAGE_BY_TAG" \ + --cache-from type=registry,ref="$LATEST_IMAGE" \ + -t "$IMAGE_BY_HASH" \ + -t "$IMAGE_BY_TAG" \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + --build-arg BUILD_SOURCE="${{ steps.tag.outputs.TAG }}" \ + --build-arg AMP_URL="${{ steps.deploy_config.outputs.AMP_URL }}" \ + --build-arg AMP_PULL_REQUEST="${{ steps.deploy_config.outputs.PR_NUMBER || '' }}" \ + --build-arg AMP_BRANCH="${GITHUB_REF#refs/heads/}" \ + --build-arg AMP_REGISTRY_PRIVATE_KEY="${{ secrets.AMP_REGISTRY_PRIVATE_KEY || '' }}" \ + --build-arg SKIP_TESTS=true \ + --label git-hash="$COMMIT_HASH" \ + --load \ + amp + + echo "✅ Image built successfully" + + - name: Push Docker image to registry + run: | + COMMIT_HASH="${{ steps.commit_hash.outputs.COMMIT_HASH }}" + IMAGE_BY_HASH="${{ secrets.ECR_REGISTRY }}/amp/webapp:commit-${COMMIT_HASH:0:12}" + IMAGE_BY_TAG="${{ secrets.ECR_REGISTRY }}/amp/webapp:${{ steps.tag.outputs.TAG }}" + SKIP_BUILD="${{ steps.check_image.outputs.SKIP_BUILD }}" + + # Push commit-hash-based image (for future reuse) - only if we built it + if [ "$SKIP_BUILD" != "true" ]; then + echo "Pushing commit-hash-based image (for future reuse)..." + docker push "$IMAGE_BY_HASH" > /dev/null + fi + + # Push deployment tag image + echo "Pushing deployment tag image..." + docker push "$IMAGE_BY_TAG" > /dev/null + + if [ "$SKIP_BUILD" == "true" ]; then + echo "✅ Reused existing image (no build needed) - saved build time!" + else + echo "✅ Image built and pushed successfully" + fi + + # Set IMAGE for cleanup step + echo "IMAGE=$IMAGE_BY_TAG" >> $GITHUB_ENV + + - name: Logout from Container Registry + if: always() + run: docker logout ${{ secrets.ECR_REGISTRY }} || true + + - name: Update GitHub commit status + uses: actions/github-script@v7 + with: + script: | + github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ steps.commit_hash.outputs.COMMIT_HASH }}', + state: 'success', + target_url: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}', + description: 'Built successfully', + context: 'github-actions/build' + }); + + - name: Deploy to server + id: deploy + run: | + set -eox pipefail + + DEPLOY_HOST="${{ steps.deploy_config.outputs.DEPLOY_HOST }}" + DEPLOY_USER="${{ steps.deploy_config.outputs.DEPLOY_USER }}" + + # Set variables locally for passing to remote server + TAG="${{ steps.tag.outputs.TAG }}" + COUNTRY="${{ steps.deploy_config.outputs.COUNTRY }}" + DB_VERSION="${{ steps.db_version.outputs.DB_VERSION }}" + PG_VERSION="${{ env.PG_VERSION }}" + REGISTRY="${{ secrets.ECR_REGISTRY }}" + AWS_ACCESS_KEY_ID="${{ secrets.AWS_ACCESS_KEY_ID || '' }}" + AWS_SECRET_ACCESS_KEY="${{ secrets.AWS_SECRET_ACCESS_KEY || '' }}" + PR_NUMBER="${{ inputs.pr_number || '' }}" + + # Pass variables as environment variables through SSH + ssh $DEPLOY_USER@$DEPLOY_HOST bash -s << DEPLOY_SCRIPT + set -eo pipefail + TAG="$TAG" + COUNTRY="$COUNTRY" + DB_VERSION="$DB_VERSION" + PG_VERSION="$PG_VERSION" + REGISTRY="$REGISTRY" + AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" + AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" + PR_NUMBER="$PR_NUMBER" + + # Validate required variables + if [ -z "$TAG" ] || [ -z "$COUNTRY" ] || [ -z "$DB_VERSION" ] || [ -z "$PG_VERSION" ]; then + echo "❌ Error: One or more required variables are empty!" + echo " TAG: '$TAG'" + echo " COUNTRY: '$COUNTRY'" + echo " DB_VERSION: '$DB_VERSION'" + echo " PG_VERSION: '$PG_VERSION'" + exit 1 + fi + + # Set image name to match the registry used in the workflow + export AMP_WEBAPP_IMAGE_NAME="${REGISTRY}/amp/webapp" + + # For PRs, log the PR number + if [ -n "$PR_NUMBER" ]; then + echo "Deploying PR #$PR_NUMBER to ${COUNTRY}" + fi + + # Export AWS credentials for the script to use + export AWS_ACCESS_KEY_ID + export AWS_SECRET_ACCESS_KEY + export AWS_DEFAULT_REGION=us-east-1 + + # Check if user can access docker without sudo + if docker ps > /dev/null 2>&1; then + amp-up2 "$TAG" "$COUNTRY" "$DB_VERSION" "$PG_VERSION" + elif sudo docker ps > /dev/null 2>&1; then + # Use sudo -E to preserve environment variables + sudo -E amp-up2 "$TAG" "$COUNTRY" "$DB_VERSION" "$PG_VERSION" + else + echo "❌ Cannot access Docker daemon. User may need to be added to docker group." + echo "Run: sudo usermod -aG docker $USER" + exit 1 + fi + DEPLOY_SCRIPT + + echo "✅ Deployment successful" + + - name: Cleanup Docker image + if: always() + run: | + docker rmi "${{ env.IMAGE }}" || true \ No newline at end of file From 6dbd50568e8cf7b869c5b45e906256b9aef17ccf Mon Sep 17 00:00:00 2001 From: brianbrix Date: Mon, 27 Apr 2026 09:45:46 +0300 Subject: [PATCH 03/20] Add workflow to old codebase --- amp/Dockerfile | 2 +- amp/pom.xml | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/amp/Dockerfile b/amp/Dockerfile index 0ecda089126..98c22510522 100644 --- a/amp/Dockerfile +++ b/amp/Dockerfile @@ -134,7 +134,7 @@ COPY --from=compile-reamp /tmp/amp/TEMPLATE/reamp TEMPLATE/reamp COPY --from=compile-reampv2 /tmp/amp/TEMPLATE/reampv2 TEMPLATE/reampv2 ARG BUILD_SOURCE RUN --mount=type=cache,target=/root/.m2 \ - mvn -B test war:exploded \ + mvn -B test org.apache.maven.plugins:maven-war-plugin:2.3:exploded \ -DbuildSource=$BUILD_SOURCE \ -Djdbc.user=amp -Djdbc.password=amp122006 -Djdbc.db=amp -Djdbc.host=db \ -Djdbc.port=5432 -DdbName=postgresql -Djdbc.driverClassName=org.postgresql.Driver \ diff --git a/amp/pom.xml b/amp/pom.xml index 2be3156cdb3..1b7a7c7a71a 100644 --- a/amp/pom.xml +++ b/amp/pom.xml @@ -30,6 +30,14 @@ amp https://artifactory.dgdev.org/artifactory/amp/ + + + false + + central-fallback + Maven Central + https://repo.maven.apache.org/maven2 + @@ -1076,10 +1084,12 @@ com.google.cloud google-cloud-translate + 1.95.3 com.google.cloud google-cloud-storage + 1.111.2 org.apache.commons From 01722e93e4f6bb01da7d038822d96874fb2a0c2b Mon Sep 17 00:00:00 2001 From: brianbrix Date: Mon, 27 Apr 2026 10:01:56 +0300 Subject: [PATCH 04/20] Add workflow to old codebase --- amp/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/Dockerfile b/amp/Dockerfile index 98c22510522..0ecda089126 100644 --- a/amp/Dockerfile +++ b/amp/Dockerfile @@ -134,7 +134,7 @@ COPY --from=compile-reamp /tmp/amp/TEMPLATE/reamp TEMPLATE/reamp COPY --from=compile-reampv2 /tmp/amp/TEMPLATE/reampv2 TEMPLATE/reampv2 ARG BUILD_SOURCE RUN --mount=type=cache,target=/root/.m2 \ - mvn -B test org.apache.maven.plugins:maven-war-plugin:2.3:exploded \ + mvn -B test war:exploded \ -DbuildSource=$BUILD_SOURCE \ -Djdbc.user=amp -Djdbc.password=amp122006 -Djdbc.db=amp -Djdbc.host=db \ -Djdbc.port=5432 -DdbName=postgresql -Djdbc.driverClassName=org.postgresql.Driver \ From d487737933d78c76f68a3d9222132b14328a98ff Mon Sep 17 00:00:00 2001 From: brianbrix Date: Mon, 27 Apr 2026 13:19:15 +0300 Subject: [PATCH 05/20] Resolve failing deployment --- amp/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/amp/pom.xml b/amp/pom.xml index 1b7a7c7a71a..b54b2798de1 100644 --- a/amp/pom.xml +++ b/amp/pom.xml @@ -48,6 +48,14 @@ amp https://artifactory.dgdev.org/artifactory/amp/ + + + false + + central-maven-plugins + Maven Central + https://repo.maven.apache.org/maven2 + From f6b5be9321ac62f49fc34746d55201c99eda7de6 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Mon, 27 Apr 2026 13:40:08 +0300 Subject: [PATCH 06/20] Add funding columns to the old GGW codebase DonorFundingJob --- .../aim/helper/GlobalSettingsConstants.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/amp/WEB-INF/src/org/digijava/module/aim/helper/GlobalSettingsConstants.java b/amp/WEB-INF/src/org/digijava/module/aim/helper/GlobalSettingsConstants.java index 7cf4cdc36e5..38eff93f84f 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/helper/GlobalSettingsConstants.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/helper/GlobalSettingsConstants.java @@ -2,12 +2,40 @@ import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; public class GlobalSettingsConstants { public static final String DEFAULT_COUNTRY = "Default Country"; + public static final List ECOWAS_COUNTRIES = Arrays.asList( + "BJ", // Benin + "CI", // Côte d'Ivoire + "GM", // Gambia + "GH", // Ghana + "GN", // Guinea + "GW", // Guinea-Bissau + "LR", // Liberia + "NG", // Nigeria + "SN", // Senegal + "SL", // Sierra Leone + "TG" // Togo + ); + + public static final List GGW_COUNTRIES = Arrays.asList( + "BF", // Burkina Faso + "MR", // Mauritania + "SD", // Sudan + "TD", // Chad + "DJ", // Djibouti + "ER", // Eritrea + "ML", // Mali + "NE", // Niger + "NG", // Nigeria + "SN", // Senegal + "ET" // Ethiopia + ); public static final String FEATURE_TEMPLATE = "Feature Template"; public static final String YEAR_RANGE_START = "Year Range Start"; @@ -39,6 +67,7 @@ public class GlobalSettingsConstants { public static final String GROUP_SEPARATOR = "Default Grouping Separator"; public static final String DEFAULT_DATE_FORMAT = "Default Date Format"; + public static final String GIS_COUNTRY = "GIS Country"; public static final String SITE_DOMAIN = "Site Domain"; @@ -176,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"; @@ -246,6 +276,8 @@ public class GlobalSettingsConstants { public static final String EXEMPT_ORGANIZATION_DOCUMENTS = "Exempt organization to see documents"; public static final String AMP_DASHBOARD_URL = "AMP Dashboard URL"; + public static final String DONOR_FUNDING_ADM_LEVEL = "Donor Funding Administrative Level"; + public static final String DASHBOARD_CURRENCIES = "Dashboard Currencies"; public static final String NUMBER_OF_INDICATORS_IN_DASHBOARD = "Number of indicators in M&E Dashboard"; From d27116fe15887ffa626f27f8fa40000ce3d4c3a9 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 14:21:01 +0300 Subject: [PATCH 07/20] Add Missing global settings --- .../AMP-31057-Add-Currecies-for-dashboard.xml | 17 ++++ ...MP-31057-Donor-Funding-Admin-Levels-v1.xml | 82 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 amp/xmlpatches/4.0/AMP-31057-Add-Currecies-for-dashboard.xml create mode 100644 amp/xmlpatches/4.0/AMP-31057-Donor-Funding-Admin-Levels-v1.xml diff --git a/amp/xmlpatches/4.0/AMP-31057-Add-Currecies-for-dashboard.xml b/amp/xmlpatches/4.0/AMP-31057-Add-Currecies-for-dashboard.xml new file mode 100644 index 00000000000..635edfc0b86 --- /dev/null +++ b/amp/xmlpatches/4.0/AMP-31057-Add-Currecies-for-dashboard.xml @@ -0,0 +1,17 @@ + + + AMP-31057 + bmokandu + Dashboard Currencies + + + + diff --git a/amp/xmlpatches/4.0/AMP-31057-Donor-Funding-Admin-Levels-v1.xml b/amp/xmlpatches/4.0/AMP-31057-Donor-Funding-Admin-Levels-v1.xml new file mode 100644 index 00000000000..5583bcd4502 --- /dev/null +++ b/amp/xmlpatches/4.0/AMP-31057-Donor-Funding-Admin-Levels-v1.xml @@ -0,0 +1,82 @@ + + + AMP-31057 + Define possible values for Donor Funding Administrative Level and link to view + brianmokandu + + + + + \ No newline at end of file From efc1653507ee91e336821b8ebf832058037dfc87 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 15:36:41 +0300 Subject: [PATCH 08/20] Add Missing global settings --- amp/xmlpatches/4.0/GGW-207-Require-Login-For-GIS.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/xmlpatches/4.0/GGW-207-Require-Login-For-GIS.xml b/amp/xmlpatches/4.0/GGW-207-Require-Login-For-GIS.xml index 0ccc576f1ae..ffd130a7b73 100644 --- a/amp/xmlpatches/4.0/GGW-207-Require-Login-For-GIS.xml +++ b/amp/xmlpatches/4.0/GGW-207-Require-Login-For-GIS.xml @@ -9,7 +9,7 @@ From d3f6db2cd337dcf203677be14981c9d1c4051f44 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 16:23:24 +0300 Subject: [PATCH 09/20] Failing xml patches --- amp/xmlpatches/general/views/v_nationalobjectives_level_0.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_0.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_0.xml index 827c3007660..839f7549bd8 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_0.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_0.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; From 8ac723aafc2d08421373f15eadee35bd08f69433 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 20:50:09 +0300 Subject: [PATCH 10/20] Failing xml patches --- amp/xmlpatches/general/views/v_nationalobjectives_level_1.xml | 2 +- amp/xmlpatches/general/views/v_nationalobjectives_level_2.xml | 2 +- amp/xmlpatches/general/views/v_nationalobjectives_level_3.xml | 2 +- amp/xmlpatches/general/views/v_nationalobjectives_level_4.xml | 2 +- amp/xmlpatches/general/views/v_nationalobjectives_level_5.xml | 2 +- amp/xmlpatches/general/views/v_nationalobjectives_level_6.xml | 2 +- amp/xmlpatches/general/views/v_nationalobjectives_level_7.xml | 2 +- amp/xmlpatches/general/views/v_nationalobjectives_level_8.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_1.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_1.xml index 268d66192de..98d49bf32f2 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_1.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_1.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_2.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_2.xml index a81f55adb32..6c754f8042e 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_2.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_2.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_3.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_3.xml index b6c697b1c9c..c3705dddd78 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_3.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_3.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_4.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_4.xml index a915f3329e4..79a86fa7c9e 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_4.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_4.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id4 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_5.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_5.xml index 9bf131970d8..ebcdb46020c 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_5.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_5.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id5 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_6.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_6.xml index ab635838494..b7e4438b943 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_6.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_6.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id6 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_7.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_7.xml index a524ba94386..ce4935cea71 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_7.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_7.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id7 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_nationalobjectives_level_8.xml b/amp/xmlpatches/general/views/v_nationalobjectives_level_8.xml index 88c823b8f58..e5683dd90a8 100644 --- a/amp/xmlpatches/general/views/v_nationalobjectives_level_8.xml +++ b/amp/xmlpatches/general/views/v_nationalobjectives_level_8.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id8 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'National Plan Objective' - GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id, level.name ) v ON aav.amp_activity_id = v.amp_activity_id; From 6e8d5ec9342478a4e17fa447f79ef92de8a2447f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 21:20:57 +0300 Subject: [PATCH 11/20] Failing xml patches --- amp/xmlpatches/general/views/v_pledges_npd_objectives.xml | 2 +- .../general/views/v_pledges_npd_objectives_level_1.xml | 2 +- amp/xmlpatches/general/views/v_pledges_programs.xml | 2 +- amp/xmlpatches/general/views/v_pledges_programs_level_1.xml | 2 +- amp/xmlpatches/general/views/v_pledges_secondary_programs.xml | 2 +- .../general/views/v_pledges_secondary_programs_level_1.xml | 2 +- amp/xmlpatches/general/views/v_pledges_tertiary_programs.xml | 2 +- .../general/views/v_pledges_tertiary_programs_level_1.xml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/amp/xmlpatches/general/views/v_pledges_npd_objectives.xml b/amp/xmlpatches/general/views/v_pledges_npd_objectives.xml index 18e4044d41e..6ad31d590e8 100644 --- a/amp/xmlpatches/general/views/v_pledges_npd_objectives.xml +++ b/amp/xmlpatches/general/views/v_pledges_npd_objectives.xml @@ -31,7 +31,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 WHERE oapl.program_setting_name = 'National Plan Objective' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id; + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name; diff --git a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_1.xml b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_1.xml index c7d82a2428b..5bd4cbf9e7d 100644 --- a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_1.xml +++ b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_1.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 WHERE oapl.program_setting_name = 'National Plan Objective' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_programs.xml b/amp/xmlpatches/general/views/v_pledges_programs.xml index bfafcffda33..a90382221bc 100644 --- a/amp/xmlpatches/general/views/v_pledges_programs.xml +++ b/amp/xmlpatches/general/views/v_pledges_programs.xml @@ -31,7 +31,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 WHERE oapl.program_setting_name = 'Primary Program' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id; + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name; diff --git a/amp/xmlpatches/general/views/v_pledges_programs_level_1.xml b/amp/xmlpatches/general/views/v_pledges_programs_level_1.xml index 48ca788db58..71df1aec468 100644 --- a/amp/xmlpatches/general/views/v_pledges_programs_level_1.xml +++ b/amp/xmlpatches/general/views/v_pledges_programs_level_1.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 WHERE oapl.program_setting_name = 'Primary Program' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_secondary_programs.xml b/amp/xmlpatches/general/views/v_pledges_secondary_programs.xml index d4ff2af04ef..0ecebc40a76 100644 --- a/amp/xmlpatches/general/views/v_pledges_secondary_programs.xml +++ b/amp/xmlpatches/general/views/v_pledges_secondary_programs.xml @@ -31,7 +31,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 WHERE oapl.program_setting_name = 'Secondary Program' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id; + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name; diff --git a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_1.xml b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_1.xml index 87db46d71c3..e5bf3a09207 100644 --- a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_1.xml +++ b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_1.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 WHERE oapl.program_setting_name = 'Secondary Program' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_tertiary_programs.xml b/amp/xmlpatches/general/views/v_pledges_tertiary_programs.xml index 3ae61e459d3..20d3bbadd4f 100644 --- a/amp/xmlpatches/general/views/v_pledges_tertiary_programs.xml +++ b/amp/xmlpatches/general/views/v_pledges_tertiary_programs.xml @@ -31,7 +31,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 WHERE oapl.program_setting_name = 'Tertiary Program' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id; + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name; diff --git a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_1.xml b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_1.xml index e7896b5546a..b6d28bcd42f 100644 --- a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_1.xml +++ b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_1.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 WHERE oapl.program_setting_name = 'Tertiary Program' - GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id1, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; From 848923945a79bb472deaf72499165dc0da517290 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 21:36:05 +0300 Subject: [PATCH 12/20] Failing xml patches --- .../general/views/v_pledges_npd_objectives_level_0.xml | 2 +- .../general/views/v_pledges_npd_objectives_level_2.xml | 2 +- .../general/views/v_pledges_npd_objectives_level_3.xml | 2 +- amp/xmlpatches/general/views/v_pledges_programs_level_0.xml | 2 +- amp/xmlpatches/general/views/v_pledges_programs_level_2.xml | 2 +- amp/xmlpatches/general/views/v_pledges_programs_level_3.xml | 2 +- .../general/views/v_pledges_secondary_programs_level_0.xml | 2 +- .../general/views/v_pledges_secondary_programs_level_2.xml | 2 +- .../general/views/v_pledges_secondary_programs_level_3.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_2.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_4.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_5.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_6.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_7.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_8.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_0.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_1.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_2.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_3.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_4.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_5.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_8.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_0.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_1.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_2.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_3.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_4.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_5.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_6.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_7.xml | 2 +- 30 files changed, 30 insertions(+), 30 deletions(-) diff --git a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_0.xml b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_0.xml index 0d6e8d9f1e5..2b4b808065e 100644 --- a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_0.xml +++ b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_0.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 WHERE oapl.program_setting_name = 'National Plan Objective' - GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_2.xml b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_2.xml index 2b5d7d39b8b..8a55358e85b 100644 --- a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_2.xml +++ b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_2.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 WHERE oapl.program_setting_name = 'National Plan Objective' - GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_3.xml b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_3.xml index 65980052765..2edd0736db0 100644 --- a/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_3.xml +++ b/amp/xmlpatches/general/views/v_pledges_npd_objectives_level_3.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 WHERE oapl.program_setting_name = 'National Plan Objective' - GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_programs_level_0.xml b/amp/xmlpatches/general/views/v_pledges_programs_level_0.xml index e56c3588810..ebeb3cbba90 100644 --- a/amp/xmlpatches/general/views/v_pledges_programs_level_0.xml +++ b/amp/xmlpatches/general/views/v_pledges_programs_level_0.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 WHERE oapl.program_setting_name = 'Primary Program' - GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_programs_level_2.xml b/amp/xmlpatches/general/views/v_pledges_programs_level_2.xml index 97ba251fd1f..e189723d111 100644 --- a/amp/xmlpatches/general/views/v_pledges_programs_level_2.xml +++ b/amp/xmlpatches/general/views/v_pledges_programs_level_2.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 WHERE oapl.program_setting_name = 'Primary Program' - GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_programs_level_3.xml b/amp/xmlpatches/general/views/v_pledges_programs_level_3.xml index a732265c0f3..2d245552148 100644 --- a/amp/xmlpatches/general/views/v_pledges_programs_level_3.xml +++ b/amp/xmlpatches/general/views/v_pledges_programs_level_3.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 WHERE oapl.program_setting_name = 'Primary Program' - GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_0.xml b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_0.xml index 8017860a911..229c072c3a9 100644 --- a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_0.xml +++ b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_0.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 WHERE oapl.program_setting_name = 'Secondary Program' - GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_2.xml b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_2.xml index 684db3edca3..e092df828da 100644 --- a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_2.xml +++ b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_2.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 WHERE oapl.program_setting_name = 'Secondary Program' - GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_3.xml b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_3.xml index ef03abd7b21..4d080b480fe 100644 --- a/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_3.xml +++ b/amp/xmlpatches/general/views/v_pledges_secondary_programs_level_3.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 WHERE oapl.program_setting_name = 'Secondary Program' - GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_2.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_2.xml index 92d78d00f39..b310437c7e4 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_2.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_2.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_4.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_4.xml index 2a97e26f7fb..057512fa062 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_4.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_4.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id4 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_5.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_5.xml index 4c781881b58..7155ee5323b 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_5.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_5.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id5 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_6.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_6.xml index fcd99393466..26dd81407a8 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_6.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_6.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id6 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_7.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_7.xml index f9986b64945..6b838747cf2 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_7.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_7.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id7 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_8.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_8.xml index 94fb913979c..a5fb5a4cee3 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_8.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_8.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id8 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_0.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_0.xml index 3e05d7c40b4..8435319bf85 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_0.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_0.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_1.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_1.xml index e60700d8cc8..7a7e27231d3 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_1.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_1.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_2.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_2.xml index 000e2d04cca..7f33b0f65c1 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_2.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_2.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_3.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_3.xml index c0093dfd678..4bb757d9c19 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_3.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_3.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_4.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_4.xml index e7868c046f8..27af0c8c42f 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_4.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_4.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id4 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_5.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_5.xml index 9412de3b5fe..1063594fa54 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_5.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_5.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id5 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_8.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_8.xml index 33cafa1ad86..bdde7d162f9 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_8.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_8.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id8 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_0.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_0.xml index b344695b7ad..0fbe83769ea 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_0.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_0.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_1.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_1.xml index 4ba65afd1e9..4bbd546de4b 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_1.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_1.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_2.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_2.xml index 162d1abef7f..2d8bba1121b 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_2.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_2.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id2, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_3.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_3.xml index ab19876e97b..710e825fbfc 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_3.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_3.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_4.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_4.xml index f2127b73abf..4f028a124b1 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_4.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_4.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id4 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id4, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_5.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_5.xml index deb555d9017..93760fad3f4 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_5.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_5.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id5 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id5, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_6.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_6.xml index 0d8abe9f794..4e4bdf70eaf 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_6.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_6.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id6 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_7.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_7.xml index 40f49855d0e..1b7ac1d1b71 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_7.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_7.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id7 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; From 47375030dd679e4f472be7baf749adaa9c65c420 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 22:03:24 +0300 Subject: [PATCH 13/20] Failing xml patches --- .../general/views/v_pledges_tertiary_programs_level_0.xml | 2 +- .../general/views/v_pledges_tertiary_programs_level_2.xml | 2 +- .../general/views/v_pledges_tertiary_programs_level_3.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_0.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_1.xml | 2 +- amp/xmlpatches/general/views/v_primaryprogram_level_3.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_6.xml | 2 +- amp/xmlpatches/general/views/v_secondaryprogram_level_7.xml | 2 +- amp/xmlpatches/general/views/v_tertiaryprogram_level_8.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_0.xml b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_0.xml index ff7807651f0..349fce4a87d 100644 --- a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_0.xml +++ b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_0.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 WHERE oapl.program_setting_name = 'Tertiary Program' - GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id0, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_2.xml b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_2.xml index 0debfd561c4..e30b0352099 100644 --- a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_2.xml +++ b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_2.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id2 WHERE oapl.program_setting_name = 'Tertiary Program' - GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id2, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_3.xml b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_3.xml index 788126e41b2..b856e2fb41e 100644 --- a/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_3.xml +++ b/amp/xmlpatches/general/views/v_pledges_tertiary_programs_level_3.xml @@ -39,7 +39,7 @@ JOIN all_programs_with_levels oapl ON afpp.amp_program_id = oapl.amp_theme_id LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 WHERE oapl.program_setting_name = 'Tertiary Program' - GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id + GROUP BY afpp.pledge_id, apl.id3, level.amp_theme_id,level.name ) v ON afp.id = v.pledge_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_0.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_0.xml index 1f3e0636d05..4711e3b10e6 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_0.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_0.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id0 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id0, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_1.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_1.xml index 5266f93e404..6fe87cde9f8 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_1.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_1.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id1 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id1, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_primaryprogram_level_3.xml b/amp/xmlpatches/general/views/v_primaryprogram_level_3.xml index 2e84000deb7..e2a9ae35893 100644 --- a/amp/xmlpatches/general/views/v_primaryprogram_level_3.xml +++ b/amp/xmlpatches/general/views/v_primaryprogram_level_3.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id3 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Primary Program' - GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id3, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_6.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_6.xml index 79220974909..263c8a1a61d 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_6.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_6.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id6 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id6, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_secondaryprogram_level_7.xml b/amp/xmlpatches/general/views/v_secondaryprogram_level_7.xml index c8649ea985d..95d09240758 100644 --- a/amp/xmlpatches/general/views/v_secondaryprogram_level_7.xml +++ b/amp/xmlpatches/general/views/v_secondaryprogram_level_7.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id7 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Secondary Program' - GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id7, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; diff --git a/amp/xmlpatches/general/views/v_tertiaryprogram_level_8.xml b/amp/xmlpatches/general/views/v_tertiaryprogram_level_8.xml index 41d29a41950..afa15978fe7 100644 --- a/amp/xmlpatches/general/views/v_tertiaryprogram_level_8.xml +++ b/amp/xmlpatches/general/views/v_tertiaryprogram_level_8.xml @@ -35,7 +35,7 @@ LEFT JOIN amp_theme level ON level.amp_theme_id = apl.id8 JOIN amp_program_settings aps ON aps.amp_program_settings_id = a.program_setting WHERE aps.name = 'Tertiary Program' - GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id + GROUP BY a.amp_activity_id, apl.id8, level.amp_theme_id,level.name ) v ON aav.amp_activity_id = v.amp_activity_id; From 2d506e6d2ed3bed7233f18a87291ac05a0210fb9 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Tue, 28 Apr 2026 23:18:24 +0300 Subject: [PATCH 14/20] Revert donor job columns --- .../amp/newreports/ReportsDashboard.java | 97 +--- .../message/jobs/AmpDonorFundingJob.java | 458 +++--------------- 2 files changed, 89 insertions(+), 466 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java b/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java index 14be3904c3a..95ae7b79ebf 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/newreports/ReportsDashboard.java @@ -4,31 +4,21 @@ public class ReportsDashboard { private String donorAgency; - private String implementingAgency; - private String responsibleOrganization; private String pillar; private String implementationLevel; - private String procurementSystem; - private String location; + + private String country; private String status; private String reportingSystem; private String typeOfAssistance; private String currency; - //private String year; + private String year; private BigDecimal actualCommitment; private BigDecimal actualDisbursement; - private Long activityCount; - // Comma separated AMP activity IDs aggregated under the current hierarchy path - private String activityIds; - private String projectTitle; - // Translations for field labels: List of maps, each map contains "label" key and locale codes as keys - private java.util.List> translations; - private String secondarySector; public ReportsDashboard() { this.actualCommitment = BigDecimal.ZERO; this.actualDisbursement = BigDecimal.ZERO; - this.activityCount = 0L; } @@ -40,14 +30,6 @@ public void setDonorAgency(String donorAgency) { this.donorAgency = donorAgency; } - public String getImplementingAgency() { - return implementingAgency; - } - - public void setImplementingAgency(String implementingAgency) { - this.implementingAgency = implementingAgency; - } - public String getPillar() { return pillar; } @@ -56,23 +38,14 @@ public void setPillar(String pillar) { this.pillar = pillar; } - public String getActivityIds() { - return activityIds; + public String getYear() { + return year; } - public void setActivityIds(String activityIds) { - this.activityIds = activityIds; + public void setYear(String year) { + this.year = year; } - /* - public String getYear() { - return year; - } - - public void setYear(String year) { - this.year = year; - } - */ public BigDecimal getActualCommitment() { return actualCommitment; } @@ -81,12 +54,12 @@ public void setActualCommitment(BigDecimal actualCommitment) { this.actualCommitment = actualCommitment; } - public String getLocation() { - return location; + public String getCountry() { + return country; } - public void setLocation(String location) { - this.location = location; + public void setCountry(String country) { + this.country = country; } public String getImplementationLevel() { @@ -145,50 +118,4 @@ public String getTypeOfAssistance() { public void setTypeOfAssistance(String typeOfAssistance) { this.typeOfAssistance = typeOfAssistance; } - - public Long getActivityCount() { - return activityCount; - } - - public void setActivityCount(Long activityCount) { - this.activityCount = activityCount; - } - public String getProcurementSystem() { - return procurementSystem; - } - public void setProcurementSystem(String procurementSystem) { - this.procurementSystem = procurementSystem; - } - - public String getResponsibleOrganization() { - return responsibleOrganization; - } - - public void setResponsibleOrganization(String responsibleOrganization) { - this.responsibleOrganization = responsibleOrganization; - } - - public String getProjectTitle() { - return projectTitle; - } - - public void setProjectTitle(String projectTitle) { - this.projectTitle = projectTitle; - } - - public java.util.List> getTranslations() { - return translations; - } - - public void setTranslations(java.util.List> translations) { - this.translations = translations; - } - - public String getSecondarySector() { - return secondarySector; - } - - public void setSecondarySector(String secondarySector) { - this.secondarySector = secondarySector; - } -} \ No newline at end of file +} diff --git a/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java b/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java index 4ff91788e2f..ba749d1c991 100644 --- a/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java +++ b/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java @@ -5,32 +5,10 @@ import org.dgfoundation.amp.ar.ArConstants; import org.dgfoundation.amp.ar.ColumnConstants; import org.dgfoundation.amp.ar.MeasureConstants; -import org.dgfoundation.amp.newreports.AmountCell; -import org.dgfoundation.amp.newreports.FilterRule; -import org.dgfoundation.amp.newreports.GeneratedReport; -import org.dgfoundation.amp.newreports.GroupingCriteria; -import org.dgfoundation.amp.newreports.IntCell; -import org.dgfoundation.amp.newreports.ReportArea; -import org.dgfoundation.amp.newreports.ReportCell; -import org.dgfoundation.amp.newreports.ReportColumn; -import org.dgfoundation.amp.newreports.ReportElement; -import org.dgfoundation.amp.newreports.ReportFiltersImpl; -import org.dgfoundation.amp.newreports.ReportMeasure; -import org.dgfoundation.amp.newreports.ReportOutputColumn; -import org.dgfoundation.amp.newreports.ReportSettingsImpl; -import org.dgfoundation.amp.newreports.ReportSpecificationImpl; -import org.dgfoundation.amp.newreports.ReportsDashboard; -import org.dgfoundation.amp.newreports.TextCell; +import org.dgfoundation.amp.newreports.*; import org.digijava.kernel.ampapi.endpoints.common.EndpointUtils; -import org.digijava.kernel.entity.Locale; -import org.digijava.kernel.persistence.PersistenceManager; -import org.digijava.kernel.request.Site; -import org.digijava.kernel.request.TLSUtils; -import org.digijava.kernel.translator.TranslatorWorker; import org.digijava.module.aim.helper.GlobalSettingsConstants; import org.digijava.module.aim.util.FeaturesUtil; -import org.hibernate.Session; -import org.hibernate.query.Query; import org.jetbrains.annotations.NotNull; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; @@ -38,41 +16,27 @@ import java.io.IOException; import java.io.OutputStream; -import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; public class AmpDonorFundingJob extends ConnectionCleaningJob implements StatefulJob { protected static final Logger logger = Logger.getLogger(AmpDonorFundingJob.class); - private List currencies = new ArrayList<>(java.util.Arrays.asList("USD", "EUR")); - /*private String reportName = "preview report"; - private Integer reportType = ArConstants.DONOR_TYPE;*/ @Override public void executeInternal(JobExecutionContext context) throws JobExecutionException { - //TODO make currency configurable - AmpJobsUtil.populateRequest(); - Long ampTeamId = FeaturesUtil - .getGlobalSettingValueLong(GlobalSettingsConstants.WORKSPACE_TO_RUN_REPORT_FROM_JOB); - AmpJobsUtil.setTeamForNonRequestReport(ampTeamId); - List ampDashboardFundingCombined = new ArrayList<>(); - currencies = Objects.requireNonNull(FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DASHBOARD_CURRENCIES)).isEmpty() ? - currencies : - Arrays.asList(Objects.requireNonNull(FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DASHBOARD_CURRENCIES)).split("\\|")); - currencies.forEach(currency -> ampDashboardFundingCombined.addAll(getFundingByCurrency(currency))); - //List ampDashboardFundingCombinedXDR = getFundingByCurrency("XDR"); - - // Get translations for all fields (once, not per record) - List> translations = getTranslations(); + List ampDashboardFundingCombinedUSD = getFundingByCurrency("USD"); + List ampDashboardFundingCombinedEUR = getFundingByCurrency("EUR"); + List ampDashboardFundingCombinedXDR = getFundingByCurrency("XDR"); String serverUrl = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.AMP_DASHBOARD_URL); - assert serverUrl != null; - serverUrl = serverUrl.endsWith("/") ? serverUrl + "amp-funding/importDonorFunding" : serverUrl + "/amp-funding/importDonorFunding"; - sendReportsToServer(ampDashboardFundingCombined, translations, serverUrl); + if (serverUrl != null) { + ampDashboardFundingCombinedUSD.addAll(ampDashboardFundingCombinedEUR); + ampDashboardFundingCombinedUSD.addAll(ampDashboardFundingCombinedXDR); + } + sendReportsToServer(ampDashboardFundingCombinedUSD, serverUrl); } @NotNull @@ -81,166 +45,77 @@ private List getFundingByCurrency(String currencyCode) { List ampDashboardFunding = processReportData(fundingReport, currencyCode); // The ampDashboardFunding data contains objects for commitments and disbursment differently in // separate objects. We need to combine them in same object combining commitment and disbursment values. - //Make year configurable - //+ "|" + report.getYear() - return new ArrayList<>(ampDashboardFunding.stream() + List ampDashboardFundingCombined = ampDashboardFunding.stream() .collect(Collectors.toMap( - report -> report.getDonorAgency() - + "|" + report.getImplementingAgency() - + "|" + report.getPillar() - + "|" + report.getLocation() - + "|" + report.getImplementationLevel() - + "|" + report.getStatus() - //+ "|" + report.getYear() - + "|" + report.getReportingSystem() - + "|" + report.getTypeOfAssistance() - + "|" + report.getProcurementSystem() - + "|" + report.getResponsibleOrganization() - + "|" + report.getSecondarySector(), + report -> report.getDonorAgency() + "|" + report.getPillar() + "|" + + report.getCountry() + "|" + report.getImplementationLevel() + "|" + + report.getStatus() + "|" + report.getYear() + "|" + report.getReportingSystem() + + "|" + report.getTypeOfAssistance(), report -> report, (report1, report2) -> { report1.sumWith(report2); return report1; })) - .values()); + .values() + .stream() + .collect(Collectors.toList()); + return ampDashboardFundingCombined; } private List processReportData(GeneratedReport report, String currencyCode) { - logger.info("Processing report data for currency: " + currencyCode); - logger.info("Number of leaf headers: " + report.leafHeaders.size()); - // Find columns by name for robustness (works regardless of order) - // Note: Indices are based on the order in addColumnsToSpecification: - // Hierarchies: 0-10, then AMP_ID (11), ACTIVITY_COUNT (12), measures (13-14) - ReportOutputColumn donorAgency = findColumnByName(report.leafHeaders, ColumnConstants.DONOR_AGENCY, 0); - ReportOutputColumn implementingAgency = findColumnByName(report.leafHeaders, ColumnConstants.IMPLEMENTING_AGENCY, 1); - ReportOutputColumn procurementSystemAgency = findColumnByName(report.leafHeaders, ColumnConstants.PROCUREMENT_SYSTEM, 2); - ReportOutputColumn pilar = findColumnByName(report.leafHeaders, ColumnConstants.NATIONAL_PLANNING_OBJECTIVES_LEVEL_1, 3); - ReportOutputColumn implementationLevel = findColumnByName(report.leafHeaders, ColumnConstants.IMPLEMENTATION_LEVEL, 4); - String location_adm_level = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DONOR_FUNDING_ADM_LEVEL); - ReportOutputColumn impLocation = findColumnByName(report.leafHeaders, location_adm_level, 5); - ReportOutputColumn status = findColumnByName(report.leafHeaders, ColumnConstants.STATUS, 6); - ReportOutputColumn typeOfAssistance = findColumnByName(report.leafHeaders, ColumnConstants.TYPE_OF_ASSISTANCE, 7); - ReportOutputColumn reportingSystem = findColumnByName(report.leafHeaders, ColumnConstants.PRIMARY_SECTOR, 8); // Also called Forum - ReportOutputColumn responsibleOrg = findColumnByName(report.leafHeaders, ColumnConstants.RESPONSIBLE_ORGANIZATION, 9); - ReportOutputColumn secondarySector = findColumnByName(report.leafHeaders, ColumnConstants.SECONDARY_SECTOR, 10); - ReportOutputColumn ampId = findColumnByName(report.leafHeaders, ColumnConstants.AMP_ID, 11); // AMP_ID is now after hierarchies + ReportOutputColumn donorAgency = report.leafHeaders.get(0); + ReportOutputColumn pilar = report.leafHeaders.get(1); + ReportOutputColumn implementationLevel = report.leafHeaders.get(2); + ReportOutputColumn country = report.leafHeaders.get(3); + ReportOutputColumn status = report.leafHeaders.get(4); + ReportOutputColumn typeOfAssistance = report.leafHeaders.get(5); + ReportOutputColumn reportingSystem = report.leafHeaders.get(6); // Also called Forum - List ampDashboardFunding = new ArrayList<>(); + List ampDashboardFunding = new ArrayList<>(); for (ReportArea child : report.reportContents.getChildren()) { TextCell donorAgencyCell = (TextCell) child.getContents().get(donorAgency); - if (child.getChildren() != null) { - for (ReportArea implementingAgencyData : child.getChildren()) { - TextCell implementingAgencyCell = (TextCell) implementingAgencyData.getContents().get(implementingAgency); - - if (implementingAgencyData.getChildren() != null) { - for (ReportArea procurementSystemAgencyData : implementingAgencyData.getChildren()) { - TextCell procurementSystemAgencyCell = (TextCell) procurementSystemAgencyData.getContents().get(procurementSystemAgency); - - if (procurementSystemAgencyData.getChildren() != null) { - for (ReportArea pilarData : procurementSystemAgencyData.getChildren()) { - TextCell pilarCell = (TextCell) pilarData.getContents().get(pilar); - - if (pilarData.getChildren() != null) { - for (ReportArea implLevel : pilarData.getChildren()) { - TextCell implLevelCell = (TextCell) implLevel.getContents().get(implementationLevel); - - if (implLevel.getChildren() != null) { - for (ReportArea location : implLevel.getChildren()) { - TextCell locationCell = (TextCell) location.getContents().get(impLocation); - - if (location.getChildren() != null) { - for (ReportArea statusData : location.getChildren()) { - TextCell statusCell = (TextCell) statusData.getContents().get(status); - - if (statusData.getChildren() != null) { - for (ReportArea typeOfAssistanceData : statusData.getChildren()) { - TextCell typeOfAssistanceCell = (TextCell) typeOfAssistanceData.getContents().get(typeOfAssistance); - - if (typeOfAssistanceData.getChildren() != null) { - for (ReportArea primarySectorData : typeOfAssistanceData.getChildren()) { - TextCell reportingSystemCell = (TextCell) primarySectorData.getContents().get(reportingSystem); - - if (primarySectorData.getChildren() != null) { - for (ReportArea responsibleOrgData : primarySectorData.getChildren()) { - TextCell responsibleOrgCell = (TextCell) responsibleOrgData.getContents().get(responsibleOrg); - - if (responsibleOrgData.getChildren() != null) { - for (ReportArea secondarySectorData : responsibleOrgData.getChildren()) { - TextCell secondarySectorCell = (TextCell) secondarySectorData.getContents().get(secondarySector); - - Long activityCount = 0L; - // collect activity count - for (Map.Entry content : responsibleOrgData.getContents().entrySet()) { - ReportOutputColumn col = content.getKey(); - if (col.originalColumnName.equals(ColumnConstants.ACTIVITY_COUNT)) { - IntCell amount = (IntCell) content.getValue(); - if (amount != null && amount.value != null) { - activityCount = (Long) amount.value; - } - } - } - // gather AMP IDs from children (leaf nodes) - List ampIdsList = new ArrayList<>(); - if (responsibleOrgData.getChildren() != null) { - for (ReportArea ampIdData : responsibleOrgData.getChildren()) { - TextCell ampIdCell = (TextCell) ampIdData.getContents().get(ampId); - if (ampIdCell != null && ampIdCell.value != null) { - ampIdsList.add(ampIdCell.value.toString()); - } - } - } - // fallback if AMP ID directly on this node and no children - if (ampIdsList.isEmpty()) { - TextCell directAmpIdCell = (TextCell) responsibleOrgData.getContents().get(ampId); - if (directAmpIdCell != null && directAmpIdCell.value != null) { - ampIdsList.add(directAmpIdCell.value.toString()); - } - } - String ampIdsJoined = String.join(",", ampIdsList); - - // now process measures - for (Map.Entry content : responsibleOrgData.getContents().entrySet()) { - ReportOutputColumn col = content.getKey(); - if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || col.originalColumnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { - if (col.parentColumn != null && col.parentColumn.originalColumnName.equals("Totals")) { - ReportsDashboard fundingReport = new ReportsDashboard(); - fundingReport.setDonorAgency(donorAgencyCell != null ? donorAgencyCell.value.toString() : null); - fundingReport.setImplementingAgency(implementingAgencyCell != null ? implementingAgencyCell.value.toString() : null); - fundingReport.setPillar(pilarCell != null ? pilarCell.value.toString() : null); - fundingReport.setLocation(locationCell != null ? locationCell.value.toString() : null); - fundingReport.setImplementationLevel(implLevelCell != null ? implLevelCell.value.toString() : null); - fundingReport.setStatus(statusCell != null ? statusCell.value.toString() : null); - fundingReport.setReportingSystem(reportingSystemCell != null ? reportingSystemCell.value.toString() : null); - fundingReport.setTypeOfAssistance(typeOfAssistanceCell != null ? typeOfAssistanceCell.value.toString() : null); - fundingReport.setProcurementSystem(procurementSystemAgencyCell != null ? procurementSystemAgencyCell.value.toString() : null); - fundingReport.setResponsibleOrganization(responsibleOrgCell != null ? responsibleOrgCell.value.toString() : null); - fundingReport.setSecondarySector(secondarySectorCell != null ? secondarySectorCell.value.toString() : null); - fundingReport.setActivityCount(activityCount); - fundingReport.setCurrency(currencyCode); - fundingReport.setActivityIds(ampIdsJoined); - AmountCell amount = (AmountCell) content.getValue(); - if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS)) { - fundingReport.setActualCommitment(amount.extractValue()); - } else { - fundingReport.setActualDisbursement(amount.extractValue()); - } - ampDashboardFunding.add(fundingReport); - } - } - } - } - } - } - } - } - } - } - } - } + for (ReportArea pilarData : child.getChildren()) { + TextCell pilarCell = (TextCell) pilarData.getContents().get(pilar); + for (ReportArea implLevel : pilarData.getChildren()) { + TextCell implLevelCell = (TextCell) implLevel.getContents().get(implementationLevel); + for (ReportArea location : implLevel.getChildren()) { + TextCell countryCell = (TextCell) location.getContents().get(country); + for (ReportArea statusData : location.getChildren()) { + TextCell statusCell = (TextCell) statusData.getContents().get(status); + for (ReportArea typeOfAssistanceData : statusData.getChildren()) { + //for (ReportArea reportSystemData : statusData.getChildren()) { + TextCell typeOfAssistanceCell= (TextCell) typeOfAssistanceData.getContents().get(typeOfAssistance); + //TextCell reportSystemCell = (TextCell) reportSystemData.getContents().get(reportingSystem); + for (ReportArea reportSystemData : typeOfAssistanceData.getChildren()) { + //for (ReportArea typeOfAssistanceData : reportSystemData.getChildren()) { + TextCell reportSystemCell = (TextCell) reportSystemData.getContents().get(reportingSystem); + //TextCell typeOfAssistanceCell = (TextCell) typeOfAssistanceData.getContents().get(typeOfAssistance); + for (Map.Entry content : reportSystemData.getContents().entrySet()) { + + + ReportOutputColumn col = content.getKey(); + if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || col.originalColumnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { + if (!col.parentColumn.originalColumnName.equals("Totals")) { + ReportsDashboard fundingReport = new ReportsDashboard(); + fundingReport.setDonorAgency(donorAgencyCell.value.toString()); + fundingReport.setPillar(pilarCell.value.toString()); + fundingReport.setCountry(countryCell.value.toString()); + fundingReport.setImplementationLevel(implLevelCell.value.toString()); + fundingReport.setStatus(statusCell.value.toString()); + fundingReport.setReportingSystem(reportSystemCell.value.toString()); + fundingReport.setTypeOfAssistance(typeOfAssistanceCell.value.toString()); + fundingReport.setYear(col.parentColumn.originalColumnName); + AmountCell amount = (AmountCell) content.getValue(); + if (col.originalColumnName.equals(MeasureConstants.ACTUAL_COMMITMENTS)) { + fundingReport.setActualCommitment(amount.extractValue()); + } else { + fundingReport.setActualDisbursement(amount.extractValue()); } + fundingReport.setCurrency(currencyCode); + ampDashboardFunding.add(fundingReport); } } } @@ -255,64 +130,6 @@ private List processReportData(GeneratedReport report, String return ampDashboardFunding; } - /** - * Find a column by name in leaf headers, with fallback to index if name doesn't match - * This provides robustness in case the order changes - */ - private ReportOutputColumn findColumnByName(List leafHeaders, String columnName, int expectedIndex) { - if (expectedIndex < leafHeaders.size()) { - ReportOutputColumn col = leafHeaders.get(expectedIndex); - // Verify the column name matches (case-insensitive comparison of originalColumnName) - if (col.originalColumnName != null && col.originalColumnName.equalsIgnoreCase(columnName)) { - return col; - } - if (columnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || columnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { - assert col.originalColumnName != null; - if (col.parentColumn.columnName.equalsIgnoreCase(columnName)) { - logger.info("Found Measure" + columnName + " at expected index " + expectedIndex); - return col; - } - } - - - // If index doesn't match, search by name - logger.warn("Column at index " + expectedIndex + " is " + col.originalColumnName + - ", expected " + columnName + ". Searching by name..."); - } - - // Fallback: search by name (works for both columns and measures) - for (ReportOutputColumn col : leafHeaders) { - if (col.originalColumnName != null && col.originalColumnName.equalsIgnoreCase(columnName)) { - logger.info("Found " + columnName + " at different position"); - return col; - } - if (columnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || columnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { - if (col.parentColumn != null && col.parentColumn.columnName.equalsIgnoreCase(columnName)) { - logger.info("Found Measure" + columnName + " at different position"); - return col; - } - } - } - - // For measures, also check if they're nested under a parent column (like "Totals") - // This handles cases where measures might be structured differently -// if (columnName.equals(MeasureConstants.ACTUAL_COMMITMENTS) || columnName.equals(MeasureConstants.ACTUAL_DISBURSEMENTS)) { -// for (ReportOutputColumn col : leafHeaders) { -// // Check if parent column exists and is "Totals", and this column's name matches the measure -// if (col.parentColumn != null && col.parentColumn.originalColumnName != null -// && ("Totals".equalsIgnoreCase(col.parentColumn.originalColumnName) -// || "TOTALS".equals(col.parentColumn.originalColumnName)) -// && col.originalColumnName != null && col.originalColumnName.equalsIgnoreCase(columnName)) { -// logger.info("Found measure column " + columnName + " under Totals"); -// return col; -// } -// } -// } - - throw new RuntimeException("Column not found: " + columnName + " in leaf headers. Available columns: " + - leafHeaders.stream().map(c -> c.originalColumnName != null ? c.originalColumnName : "null").collect(Collectors.joining(", "))); - } - private void addFilters(ReportSpecificationImpl spec) { if (spec.getFilters() == null) { spec.setFilters(new ReportFiltersImpl()); @@ -333,164 +150,43 @@ private GeneratedReport generateReport(String currencyCode) { ReportSpecificationImpl spec = new ReportSpecificationImpl("preview report", ArConstants.DONOR_TYPE); addColumnsToSpecification(spec); - spec.setSummaryReport(false); - //TODO broken by year configurable - spec.setGroupingCriteria(GroupingCriteria.GROUPING_TOTALS_ONLY); + spec.setSummaryReport(true); + spec.setGroupingCriteria(GroupingCriteria.GROUPING_YEARLY); spec.setShowOriginalCurrency(false); spec.setDisplayEmptyFundingRows(true); ReportSettingsImpl reportSettings = new ReportSettingsImpl(); spec.setSettings(reportSettings); - //addFilters(spec); + addFilters(spec); reportSettings.setCurrencyCode(currencyCode); return EndpointUtils.runReport(spec); } private void addColumnsToSpecification(ReportSpecificationImpl spec) { - String location_adm_level = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DONOR_FUNDING_ADM_LEVEL); - // Add hierarchy columns first spec.addColumn(new ReportColumn(ColumnConstants.DONOR_AGENCY)); - spec.addColumn(new ReportColumn(ColumnConstants.IMPLEMENTING_AGENCY)); - spec.addColumn(new ReportColumn(ColumnConstants.PROCUREMENT_SYSTEM)); spec.addColumn(new ReportColumn(ColumnConstants.NATIONAL_PLANNING_OBJECTIVES_LEVEL_1)); spec.addColumn(new ReportColumn(ColumnConstants.IMPLEMENTATION_LEVEL)); - spec.addColumn(new ReportColumn(location_adm_level)); + spec.addColumn(new ReportColumn(ColumnConstants.LOCATION_ADM_LEVEL_0)); spec.addColumn(new ReportColumn(ColumnConstants.STATUS)); spec.addColumn(new ReportColumn(ColumnConstants.TYPE_OF_ASSISTANCE)); - //TODO for GGW this is reporting system, for others it is Sectors - spec.addColumn(new ReportColumn(ColumnConstants.PRIMARY_SECTOR)); - spec.addColumn(new ReportColumn(ColumnConstants.RESPONSIBLE_ORGANIZATION)); - spec.addColumn(new ReportColumn(ColumnConstants.SECONDARY_SECTOR)); - // Ensure AMP ID is part of the hierarchy so commitments roll up under each activity - - // Set hierarchies - includes AMP_ID so commitments/disbursements roll up under each activity - // This is required for summary reports to include all columns in leaf headers - Set hierarchyColumns = new LinkedHashSet<>(spec.getColumns()); - spec.setHierarchies(hierarchyColumns); + spec.addColumn(new ReportColumn(ColumnConstants.REPORTING_SYSTEM)); - // Add non-hierarchy columns after setHierarchies - spec.addColumn(new ReportColumn(ColumnConstants.AMP_ID)); - spec.addColumn(new ReportColumn(ColumnConstants.ACTIVITY_COUNT)); + spec.setHierarchies(spec.getColumns()); spec.addMeasure(new ReportMeasure(MeasureConstants.ACTUAL_COMMITMENTS)); spec.addMeasure(new ReportMeasure(MeasureConstants.ACTUAL_DISBURSEMENTS)); - - logger.info("Report columns set for Donor Funding Report" + spec.getColumns().size()); } - /** - * Gets translations for all field labels (returns once, not per record) - */ - private List> getTranslations() { - List> translations = new ArrayList<>(); - - try { - // Get all available locales - List localeCodes = getAvailableLocaleCodes(); - if (localeCodes.isEmpty()) { - logger.warn("No available locales found, skipping translations"); - return translations; - } - - // Get the site - Site site = TLSUtils.getSite(); - if (site == null) { - logger.warn("No site found, skipping translations"); - return translations; - } - - // Get location column name - String locationAdmLevel = FeaturesUtil.getGlobalSettingValue(GlobalSettingsConstants.DONOR_FUNDING_ADM_LEVEL); - - // Define field mappings: field name -> column constant - Map fieldMappings = new HashMap<>(); - fieldMappings.put("donorAgency", ColumnConstants.DONOR_AGENCY); - fieldMappings.put("implementingAgency", ColumnConstants.IMPLEMENTING_AGENCY); - fieldMappings.put("pillar", ColumnConstants.NATIONAL_PLANNING_OBJECTIVES_LEVEL_1); - fieldMappings.put("location", locationAdmLevel != null ? locationAdmLevel : ColumnConstants.LOCATION); - fieldMappings.put("implementationLevel", ColumnConstants.IMPLEMENTATION_LEVEL); - fieldMappings.put("status", ColumnConstants.STATUS); - fieldMappings.put("reportingSystem", ColumnConstants.PRIMARY_SECTOR); - fieldMappings.put("typeOfAssistance", ColumnConstants.TYPE_OF_ASSISTANCE); - fieldMappings.put("procurementSystem", ColumnConstants.PROCUREMENT_SYSTEM); - fieldMappings.put("responsibleOrganization", ColumnConstants.RESPONSIBLE_ORGANIZATION); - fieldMappings.put("currency", "Currency"); - fieldMappings.put("actualCommitment", MeasureConstants.ACTUAL_COMMITMENTS); - fieldMappings.put("actualDisbursement", MeasureConstants.ACTUAL_DISBURSEMENTS); - fieldMappings.put("activityCount", ColumnConstants.ACTIVITY_COUNT); - fieldMappings.put("activityIds", "Activity IDs"); - fieldMappings.put("projectTitle", "Project Title"); - - // Generate translations for all fields once - for (Map.Entry entry : fieldMappings.entrySet()) { - String fieldName = entry.getKey(); - String label = entry.getValue(); - Map fieldTranslation = getFieldTranslations(label, localeCodes, site); - fieldTranslation.put("label", fieldName); - translations.add(fieldTranslation); - } - - logger.info("Generated translations for " + translations.size() + " fields with " + localeCodes.size() + " locales"); - } catch (Exception e) { - logger.error("Error getting translations", e); - } - - return translations; - } - - /** - * Gets all available locale codes from the database - */ - private List getAvailableLocaleCodes() { - List localeCodes = new ArrayList<>(); - Session session = null; - try { - session = PersistenceManager.getSession(); - Query query = session.createQuery("SELECT l.code FROM " + Locale.class.getName() + " l WHERE l.available = true", String.class); - List codes = query.list(); - localeCodes.addAll(codes); - } catch (Exception e) { - logger.error("Error getting available locales", e); - } - return localeCodes; - } - - /** - * Gets translations for a field label in all available locales - */ - private Map getFieldTranslations(String label, List localeCodes, Site site) { - Map translations = new HashMap<>(); - for (String localeCode : localeCodes) { - try { - String translated = TranslatorWorker.translateText(label, localeCode, site); - translations.put(localeCode, translated != null ? translated : label); - } catch (Exception e) { - logger.warn("Error translating label '" + label + "' for locale '" + localeCode + "': " + e.getMessage()); - translations.put(localeCode, label); - } - } - return translations; - } - - public static void sendReportsToServer(List ampDashboardFunding, List> translations, String serverUrl) { + public static void sendReportsToServer(List ampDashboardFunding, String serverUrl) { try { // Create a URL object with the server's endpoint URL - logger.info("Sending data to amp dashboard at: " + serverUrl); - logger.info("Number of records to send: " + ampDashboardFunding.size()); HttpURLConnection connection = getHttpURLConnection(serverUrl); - - // Create a wrapper object with reports and translations - Map submissionData = new HashMap<>(); - submissionData.put("reports", ampDashboardFunding); - submissionData.put("translations", translations); - - // Convert to JSON using a JSON library (e.g., Gson) + // Convert the ampDashboardFunding to JSON using a JSON library (e.g., Gson) Gson gson = new Gson(); - String jsonData = gson.toJson(submissionData); -// logger.info("JSON data: " + jsonData); + String jsonData = gson.toJson(ampDashboardFunding); // Get the output stream of the connection try (OutputStream os = connection.getOutputStream()) { // Write the JSON data to the output stream - os.write(jsonData.getBytes(StandardCharsets.UTF_8)); + os.write(jsonData.getBytes("UTF-8")); } // Get the HTTP response code @@ -499,10 +195,10 @@ public static void sendReportsToServer(List ampDashboardFundin // Check if the request was successful (e.g., HTTP 200 OK) if (responseCode == 200) { // The data has been successfully sent to the server - logger.info("Data sent successfully to amp dashboard. HTTP Response Code: " + responseCode); + logger.debug("Data sent successfully to amp dashboard. HTTP Response Code: " + responseCode); } else { // Handle the error condition (e.g., log an error message) - logger.info("Error sending data to amp dashboard. HTTP Response Code: " + responseCode); + logger.debug("Error sending data to amp dashboard. HTTP Response Code: " + responseCode); } // Close the connection @@ -529,4 +225,4 @@ private static HttpURLConnection getHttpURLConnection(String serverUrl) throws I connection.setDoOutput(true); return connection; } -} \ No newline at end of file +} From eaf47c3f483109b427435f9a6b139606a1a30b12 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 29 Apr 2026 08:43:22 +0300 Subject: [PATCH 15/20] Revert donor job columns --- .../src/org/digijava/module/message/jobs/AmpDonorFundingJob.java | 1 + 1 file changed, 1 insertion(+) diff --git a/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java b/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java index ba749d1c991..3f10dc32ee6 100644 --- a/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java +++ b/amp/WEB-INF/src/org/digijava/module/message/jobs/AmpDonorFundingJob.java @@ -182,6 +182,7 @@ public static void sendReportsToServer(List ampDashboardFundin // Convert the ampDashboardFunding to JSON using a JSON library (e.g., Gson) Gson gson = new Gson(); String jsonData = gson.toJson(ampDashboardFunding); + logger.info("Data sent: "+jsonData); // Get the output stream of the connection try (OutputStream os = connection.getOutputStream()) { From 7824ac6f5ff86bad70bf99aa39a0aca4b69931de Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 13 May 2026 14:25:35 +0300 Subject: [PATCH 16/20] Resolve map popup issue --- amp/repository/esrigis/view/mainmapPopup.jsp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/amp/repository/esrigis/view/mainmapPopup.jsp b/amp/repository/esrigis/view/mainmapPopup.jsp index 0c4845c492d..1e0cc80c6ef 100644 --- a/amp/repository/esrigis/view/mainmapPopup.jsp +++ b/amp/repository/esrigis/view/mainmapPopup.jsp @@ -86,15 +86,15 @@
From ccda24b2b8b0734c7c93bded7e80e33ccc20f410 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Wed, 13 May 2026 15:22:36 +0300 Subject: [PATCH 17/20] Resolve map popup issue --- amp/TEMPLATE/ampTemplate/js_2/esrigis/mainmapPopup.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/amp/TEMPLATE/ampTemplate/js_2/esrigis/mainmapPopup.js b/amp/TEMPLATE/ampTemplate/js_2/esrigis/mainmapPopup.js index e108e58e32e..66094f57c9c 100644 --- a/amp/TEMPLATE/ampTemplate/js_2/esrigis/mainmapPopup.js +++ b/amp/TEMPLATE/ampTemplate/js_2/esrigis/mainmapPopup.js @@ -71,18 +71,14 @@ function initMap() { function loadBaseMap() { map = L.map('map').setView([latitude, longitude], 7); // create the tile layer with correct attribution - var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; + var osmUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; if (isOsm) { - var subdomains = ['a', 'b', 'c']; - if (basemapurl !== undefined && basemapurl.indexOf("mqcdn") != -1) { - subdomains = ['otile1', 'otile2', 'otile3', 'otile4']; - } - var osmAttrib = 'Map data © OpenStreetMap contributors'; + var osmAttrib = 'Map data © OpenStreetMap contributors'; tileLayer = new L.TileLayer(osmUrl, { minZoom: 0, maxZoom: 16, attribution: osmAttrib, - subdomains: subdomains + subdomains: ['a', 'b', 'c'] }); } else { tileLayer = L.esri.tiledMapLayer({ From 304c77b220c68dbb497a7de319b8db4235153874 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 14 May 2026 15:11:16 +0300 Subject: [PATCH 18/20] Report engine failing --- amp/TEMPLATE/reampv2/package.json | 1 + .../reampv2/scripts/sync-build-output.js | 27 +++++++++++++++++++ .../module/aim/dbentity/AmpStructure.hbm.xml | 14 +++++----- 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 amp/TEMPLATE/reampv2/scripts/sync-build-output.js diff --git a/amp/TEMPLATE/reampv2/package.json b/amp/TEMPLATE/reampv2/package.json index b7398cacae5..1e8dea872f2 100755 --- a/amp/TEMPLATE/reampv2/package.json +++ b/amp/TEMPLATE/reampv2/package.json @@ -9,6 +9,7 @@ "scripts": { "start": "DISABLE_ESLINT=true lerna run start", "build": "DISABLE_ESLINT=true lerna run build", + "postbuild": "node scripts/sync-build-output.js", "graph": "nx graph" }, "author": "", diff --git a/amp/TEMPLATE/reampv2/scripts/sync-build-output.js b/amp/TEMPLATE/reampv2/scripts/sync-build-output.js new file mode 100644 index 00000000000..03170ffae7d --- /dev/null +++ b/amp/TEMPLATE/reampv2/scripts/sync-build-output.js @@ -0,0 +1,27 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const rootDir = path.resolve(__dirname, '..'); +const legacyBuildDir = path.join(rootDir, 'build'); +const containerBuildDir = path.join(rootDir, 'packages', 'container', 'build'); +const ampOfflineBuildDir = path.join(rootDir, 'packages', 'ampoffline', 'build'); + +const syncBuildDirectory = (sourceDir, targetDir) => { + fs.rmSync(targetDir, { recursive: true, force: true }); + fs.cpSync(sourceDir, targetDir, { recursive: true, force: true }); +}; + +if (!fs.existsSync(containerBuildDir)) { + throw new Error(`Missing required container build output: ${containerBuildDir}`); +} + +// Keep the package build locations intact, but also publish the legacy path +// still referenced by AMP menu entries and JSP redirects. +syncBuildDirectory(containerBuildDir, legacyBuildDir); +console.log(`Synced ${containerBuildDir} -> ${legacyBuildDir}`); + +if (fs.existsSync(ampOfflineBuildDir)) { + const legacyAmpOfflineDir = path.join(legacyBuildDir, 'ampoffline'); + syncBuildDirectory(ampOfflineBuildDir, legacyAmpOfflineDir); + console.log(`Synced ${ampOfflineBuildDir} -> ${legacyAmpOfflineDir}`); +} \ No newline at end of file diff --git a/amp/src/main/resources/org/digijava/module/aim/dbentity/AmpStructure.hbm.xml b/amp/src/main/resources/org/digijava/module/aim/dbentity/AmpStructure.hbm.xml index 33f1df7da44..3ffa053e6b8 100644 --- a/amp/src/main/resources/org/digijava/module/aim/dbentity/AmpStructure.hbm.xml +++ b/amp/src/main/resources/org/digijava/module/aim/dbentity/AmpStructure.hbm.xml @@ -30,13 +30,13 @@ - - - - - - - + + + + + + + From 5c83766f04e4c9da42b94821bfc1f7a0e674dd30 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 14 May 2026 15:29:12 +0300 Subject: [PATCH 19/20] Report engine failing --- .../reampv2/scripts/sync-build-output.js | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/amp/TEMPLATE/reampv2/scripts/sync-build-output.js b/amp/TEMPLATE/reampv2/scripts/sync-build-output.js index 03170ffae7d..79af52bac88 100644 --- a/amp/TEMPLATE/reampv2/scripts/sync-build-output.js +++ b/amp/TEMPLATE/reampv2/scripts/sync-build-output.js @@ -6,9 +6,30 @@ const legacyBuildDir = path.join(rootDir, 'build'); const containerBuildDir = path.join(rootDir, 'packages', 'container', 'build'); const ampOfflineBuildDir = path.join(rootDir, 'packages', 'ampoffline', 'build'); +const copyDirectory = (sourceDir, targetDir) => { + fs.mkdirSync(targetDir, { recursive: true }); + + fs.readdirSync(sourceDir, { withFileTypes: true }).forEach((entry) => { + const sourcePath = path.join(sourceDir, entry.name); + const targetPath = path.join(targetDir, entry.name); + + if (entry.isDirectory()) { + copyDirectory(sourcePath, targetPath); + return; + } + + if (entry.isSymbolicLink()) { + fs.symlinkSync(fs.readlinkSync(sourcePath), targetPath); + return; + } + + fs.copyFileSync(sourcePath, targetPath); + }); +}; + const syncBuildDirectory = (sourceDir, targetDir) => { fs.rmSync(targetDir, { recursive: true, force: true }); - fs.cpSync(sourceDir, targetDir, { recursive: true, force: true }); + copyDirectory(sourceDir, targetDir); }; if (!fs.existsSync(containerBuildDir)) { From 524528d911c54c38aa0fa9a46098cb30617697a1 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 14 May 2026 15:59:30 +0300 Subject: [PATCH 20/20] Report engine failing --- amp/TEMPLATE/reampv2/scripts/sync-build-output.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/amp/TEMPLATE/reampv2/scripts/sync-build-output.js b/amp/TEMPLATE/reampv2/scripts/sync-build-output.js index 79af52bac88..01551c743c9 100644 --- a/amp/TEMPLATE/reampv2/scripts/sync-build-output.js +++ b/amp/TEMPLATE/reampv2/scripts/sync-build-output.js @@ -3,7 +3,7 @@ const path = require('node:path'); const rootDir = path.resolve(__dirname, '..'); const legacyBuildDir = path.join(rootDir, 'build'); -const containerBuildDir = path.join(rootDir, 'packages', 'container', 'build'); +const reampv2AppBuildDir = path.join(rootDir, 'packages', 'reampv2-app', 'build'); const ampOfflineBuildDir = path.join(rootDir, 'packages', 'ampoffline', 'build'); const copyDirectory = (sourceDir, targetDir) => { @@ -32,14 +32,15 @@ const syncBuildDirectory = (sourceDir, targetDir) => { copyDirectory(sourceDir, targetDir); }; -if (!fs.existsSync(containerBuildDir)) { - throw new Error(`Missing required container build output: ${containerBuildDir}`); +if (!fs.existsSync(reampv2AppBuildDir)) { + throw new Error(`Missing required reampv2 build output: ${reampv2AppBuildDir}`); } -// Keep the package build locations intact, but also publish the legacy path -// still referenced by AMP menu entries and JSP redirects. -syncBuildDirectory(containerBuildDir, legacyBuildDir); -console.log(`Synced ${containerBuildDir} -> ${legacyBuildDir}`); +// Publish the legacy top-level path from the reampv2 app bundle because AMP +// menu entries and JSP redirects still point to hash routes like +// /TEMPLATE/reampv2/build/index.html#/report_generator. +syncBuildDirectory(reampv2AppBuildDir, legacyBuildDir); +console.log(`Synced ${reampv2AppBuildDir} -> ${legacyBuildDir}`); if (fs.existsSync(ampOfflineBuildDir)) { const legacyAmpOfflineDir = path.join(legacyBuildDir, 'ampoffline');