diff --git a/health-services/plan-service/CHANGELOG.md b/health-services/plan-service/CHANGELOG.md
index a73439d7658..bacefd74a24 100644
--- a/health-services/plan-service/CHANGELOG.md
+++ b/health-services/plan-service/CHANGELOG.md
@@ -1,6 +1,11 @@
# Changelog
All notable changes to this module will be documented in this file.
+
+## 1.0.1 - 2025-01-30
+1. Enabled support for Mixed Distribution Strategy for Registration and Service Delivery
+2. Adding additional search filters for Estimation Dashboard
+
## 1.0.0 - 2024-12-03
### Plan Service
diff --git a/health-services/plan-service/pom.xml b/health-services/plan-service/pom.xml
index c41f44a9f05..d0dba48a760 100644
--- a/health-services/plan-service/pom.xml
+++ b/health-services/plan-service/pom.xml
@@ -4,7 +4,7 @@
plan-service
jar
plan-service
- 1.0.0
+ 1.0.1
17
${java.version}
diff --git a/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java b/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java
index a2b9696b482..8597c1a2a5d 100644
--- a/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java
+++ b/health-services/plan-service/src/main/java/digit/config/ServiceConstants.java
@@ -360,10 +360,20 @@ public class ServiceConstants {
public static final String FACILITY_NAME_SEARCH_PARAMETER_KEY = "facilityName";
+ public static final String FACILITY_ID_SEARCH_PARAMETER_KEY = "facilityId";
+
public static final String FACILITY_STATUS_SEARCH_PARAMETER_KEY = "facilityStatus";
public static final String FACILITY_TYPE_SEARCH_PARAMETER_KEY = "facilityType";
+ public static final String TERRAIN_CONDITION_SEARCH_PARAMETER_KEY = "accessibilityDetails|terrain|code";
+
+ public static final String ROAD_CONDITION_SEARCH_PARAMETER_KEY = "accessibilityDetails|roadCondition|code";
+
+ public static final String SECURITY_Q1_SEARCH_PARAMETER_KEY = "securityDetails|1|code";
+
+ public static final String SECURITY_Q2_SEARCH_PARAMETER_KEY = "securityDetails|2|code";
+
public static final String COMMA_DELIMITER = ",";
public static final String SERVING_POPULATION_CODE = "servingPopulation";
@@ -374,4 +384,11 @@ public class ServiceConstants {
public static final String CONFIRMED_TARGET_POPULATION = "CONFIRMED_HCM_ADMIN_CONSOLE_TARGET_POPULATION";
+ public static final String ADDITIONAL_DETAILS_QUERY = " additional_details @> CAST( ? AS jsonb )";
+
+ public static final String JSONB_QUERY_FORMAT = "additional_details @> ?::jsonb";
+
+ public static final String AND_CONDITION = " AND ";
+
+ public static final String OR_CONDITION = " OR ";
}
diff --git a/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java b/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java
index 99e4b9487de..90cdb199bf8 100644
--- a/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java
+++ b/health-services/plan-service/src/main/java/digit/repository/impl/PlanRepositoryImpl.java
@@ -189,6 +189,7 @@ private PlanRequestDTO convertToPlanReqDTO(PlanRequest planRequest) {
.assignee(assignee)
.additionalDetails(plan.getAdditionalDetails())
.jurisdictionMapping(plan.getJurisdictionMapping())
+ .additionalFields(plan.getAdditionalFields())
.activities(plan.getActivities())
.resources(plan.getResources())
.targets(plan.getTargets())
diff --git a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java
index 5c8bc91097b..0b0c7347aa7 100644
--- a/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java
+++ b/health-services/plan-service/src/main/java/digit/repository/querybuilder/PlanQueryBuilder.java
@@ -7,10 +7,10 @@
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
+import java.util.*;
+
+import static digit.config.ServiceConstants.ADDITIONAL_DETAILS_QUERY;
+import static digit.config.ServiceConstants.FACILITY_ID_SEARCH_PARAMETER_KEY;
@Component
public class PlanQueryBuilder {
@@ -30,12 +30,14 @@ public PlanQueryBuilder(Configuration config, QueryUtil queryUtil) {
"\t plan_activity.id as plan_activity_id, plan_activity.code as plan_activity_code, plan_activity.description as plan_activity_description, plan_activity.planned_start_date as plan_activity_planned_start_date, plan_activity.planned_end_date as plan_activity_planned_end_date, plan_activity.dependencies as plan_activity_dependencies, plan_activity.plan_id as plan_activity_plan_id, plan_activity.created_by as plan_activity_created_by, plan_activity.created_time as plan_activity_created_time, plan_activity.last_modified_by as plan_activity_last_modified_by, plan_activity.last_modified_time as plan_activity_last_modified_time,\n" +
"\t plan_activity_condition.id as plan_activity_condition_id, plan_activity_condition.entity as plan_activity_condition_entity, plan_activity_condition.entity_property as plan_activity_condition_entity_property, plan_activity_condition.expression as plan_activity_condition_expression, plan_activity_condition.activity_id as plan_activity_condition_activity_id, plan_activity_condition.is_active as plan_activity_condition_is_active, plan_activity_condition.created_by as plan_activity_condition_created_by, plan_activity_condition.created_time as plan_activity_condition_created_time, plan_activity_condition.last_modified_by as plan_activity_condition_last_modified_by, plan_activity_condition.last_modified_time as plan_activity_condition_last_modified_time,\n" +
"\t plan_resource.id as plan_resource_id, plan_resource.resource_type as plan_resource_resource_type, plan_resource.estimated_number as plan_resource_estimated_number, plan_resource.plan_id as plan_resource_plan_id, plan_resource.activity_code as plan_resource_activity_code, plan_resource.created_by as plan_resource_created_by, plan_resource.created_time as plan_resource_created_time, plan_resource.last_modified_by as plan_resource_last_modified_by, plan_resource.last_modified_time as plan_resource_last_modified_time,\n" +
- "\t plan_target.id as plan_target_id, plan_target.metric as plan_target_metric, plan_target.metric_value as plan_target_metric_value, plan_target.metric_comparator as plan_target_metric_comparator, plan_target.metric_unit as plan_target_metric_unit, plan_target.plan_id as plan_target_plan_id, plan_target.activity_code as plan_target_activity_code, plan_target.created_by as plan_target_created_by, plan_target.created_time as plan_target_created_time, plan_target.last_modified_by as plan_target_last_modified_by, plan_target.last_modified_time as plan_target_last_modified_time\n" +
+ "\t plan_target.id as plan_target_id, plan_target.metric as plan_target_metric, plan_target.metric_value as plan_target_metric_value, plan_target.metric_comparator as plan_target_metric_comparator, plan_target.metric_unit as plan_target_metric_unit, plan_target.plan_id as plan_target_plan_id, plan_target.activity_code as plan_target_activity_code, plan_target.created_by as plan_target_created_by, plan_target.created_time as plan_target_created_time, plan_target.last_modified_by as plan_target_last_modified_by, plan_target.last_modified_time as plan_target_last_modified_time, \n" +
+ "\t paf.id as plan_additional_field_id, paf.plan_id as plan_additional_field_plan_id, paf.key as plan_additional_field_key, paf.value as plan_additional_field_value, paf.show_on_ui as plan_additional_field_show_on_ui, paf.editable as plan_additional_field_editable, paf.order as plan_additional_field_order \n" +
"\t FROM plan \n" +
"\t LEFT JOIN plan_activity ON plan.id = plan_activity.plan_id\n" +
"\t LEFT JOIN plan_activity_condition ON plan_activity.id = plan_activity_condition.activity_id\n" +
"\t LEFT JOIN plan_resource ON plan.id = plan_resource.plan_id\n" +
- "\t LEFT JOIN plan_target ON plan.id = plan_target.plan_id";
+ "\t LEFT JOIN plan_target ON plan.id = plan_target.plan_id\n" +
+ "\t LEFT JOIN plan_additional_field paf ON plan.id = paf.plan_id";
private static final String BULK_PLAN_UPDATE_QUERY = "UPDATE plan SET status = ?, assignee = ?, last_modified_by = ?, last_modified_time = ? WHERE id = ?";
@@ -170,6 +172,22 @@ private String buildPlanSearchQuery(PlanSearchCriteria planSearchCriteria, List<
queryUtil.addToPreparedStatement(preparedStmtList, planSearchCriteria.getJurisdiction());
}
+ if (!CollectionUtils.isEmpty(planSearchCriteria.getFiltersMap())) {
+ Map> filtersMap = planSearchCriteria.getFiltersMap();
+ for (String key : filtersMap.keySet()) {
+ if (FACILITY_ID_SEARCH_PARAMETER_KEY.equals(key)) {
+ // its for facility multi select then no need to add to preparedStmtList
+ String partialQueryJsonString = queryUtil.preparePartialJsonStringFromFilterMap(planSearchCriteria.getFiltersMap(), preparedStmtList, key);
+ builder.append(partialQueryJsonString);
+ } else {
+ // handle all other keys normally.
+ queryUtil.addClauseIfRequired(builder, preparedStmtList);
+ builder.append(ADDITIONAL_DETAILS_QUERY);
+ String partialQueryJsonString = queryUtil.preparePartialJsonStringFromFilterMap(planSearchCriteria.getFiltersMap(), preparedStmtList, key);
+ preparedStmtList.add(partialQueryJsonString);
+ }
+ }
+ }
StringBuilder countQuery = new StringBuilder();
if (isCount) {
diff --git a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java
index aa00207a741..b37561fbc3d 100644
--- a/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java
+++ b/health-services/plan-service/src/main/java/digit/repository/rowmapper/PlanRowMapper.java
@@ -30,6 +30,7 @@ public List extractData(ResultSet rs) throws SQLException, DataAccessExcep
Set conditionSet = new HashSet<>();
Set resourceSet = new HashSet<>();
Set targetSet = new HashSet<>();
+ Set additionalFieldSet = new HashSet<>();
// Traverse through result set and create plan objects
while (rs.next()) {
@@ -43,6 +44,7 @@ public List extractData(ResultSet rs) throws SQLException, DataAccessExcep
conditionSet.clear();
resourceSet.clear();
targetSet.clear();
+ additionalFieldSet.clear();
// Prepare audit details
AuditDetails auditDetails = AuditDetails.builder()
@@ -72,6 +74,7 @@ public List extractData(ResultSet rs) throws SQLException, DataAccessExcep
addActivities(rs, planEntry, activityMap, conditionSet);
addResources(rs, planEntry, resourceSet);
addTargets(rs, planEntry, targetSet);
+ addAdditionalField(rs, planEntry, additionalFieldSet);
planMap.put(planId, planEntry);
}
@@ -234,4 +237,39 @@ private void addTargets(ResultSet rs, Plan planEntry, Set targetSet) thr
targetSet.add(target.getId());
}
+
+ /**
+ * Adds a AdditionalField object to the plan entry based on the result set.
+ *
+ * @param rs The ResultSet containing the data.
+ * @param additionalFieldSet A set to keep track of added AdditionalField objects.
+ * @param planEntry The Plan entry to which the AdditionalField object will be added.
+ * @throws SQLException If an SQL error occurs.
+ */
+ private void addAdditionalField(ResultSet rs, Plan planEntry, Set additionalFieldSet) throws SQLException {
+ String additionalFieldId = rs.getString("plan_additional_field_id");
+
+ if (ObjectUtils.isEmpty(additionalFieldId) || additionalFieldSet.contains(additionalFieldId)) {
+ return;
+ }
+
+ AdditionalField additionalField = new AdditionalField();
+ additionalField.setId(rs.getString("plan_additional_field_id"));
+ additionalField.setKey(rs.getString("plan_additional_field_key"));
+ additionalField.setValue(rs.getBigDecimal("plan_additional_field_value"));
+ additionalField.setShowOnUi(rs.getBoolean("plan_additional_field_show_on_ui"));
+ additionalField.setEditable(rs.getBoolean("plan_additional_field_editable"));
+ additionalField.setOrder(rs.getInt("plan_additional_field_order"));
+
+ if (CollectionUtils.isEmpty(planEntry.getAdditionalFields())) {
+ List additionalFields = new ArrayList<>();
+ additionalFields.add(additionalField);
+ planEntry.setAdditionalFields(additionalFields);
+ } else {
+ planEntry.getAdditionalFields().add(additionalField);
+ }
+
+ additionalFieldSet.add(additionalFieldId);
+ }
+
}
diff --git a/health-services/plan-service/src/main/java/digit/service/enrichment/PlanEnricher.java b/health-services/plan-service/src/main/java/digit/service/PlanEnricher.java
similarity index 78%
rename from health-services/plan-service/src/main/java/digit/service/enrichment/PlanEnricher.java
rename to health-services/plan-service/src/main/java/digit/service/PlanEnricher.java
index 17364bfdb6d..7ab443a6f31 100644
--- a/health-services/plan-service/src/main/java/digit/service/enrichment/PlanEnricher.java
+++ b/health-services/plan-service/src/main/java/digit/service/PlanEnricher.java
@@ -1,7 +1,6 @@
-package digit.service.enrichment;
+package digit.service;
-import digit.web.models.Plan;
-import digit.web.models.PlanRequest;
+import digit.web.models.*;
import digit.web.models.boundary.BoundaryTypeHierarchy;
import digit.web.models.boundary.BoundaryTypeHierarchyDefinition;
import digit.web.models.boundary.EnrichedBoundary;
@@ -14,6 +13,8 @@
import java.util.*;
+import static digit.config.ServiceConstants.*;
+
@Component
public class PlanEnricher {
@@ -53,6 +54,10 @@ public void enrichPlanCreate(PlanRequest body) {
// Generate id for targets
body.getPlan().getTargets().forEach(target -> UUIDEnrichmentUtil.enrichRandomUuid(target, "id"));
+ // Generate id for additional fields
+ if(!CollectionUtils.isEmpty(body.getPlan().getAdditionalFields()))
+ body.getPlan().getAdditionalFields().forEach(additionalField -> UUIDEnrichmentUtil.enrichRandomUuid(additionalField, "id"));
+
// Enrich audit details
body.getPlan().setAuditDetails(AuditDetailsEnrichmentUtil
.prepareAuditDetails(body.getPlan().getAuditDetails(), body.getRequestInfo(), Boolean.TRUE));
@@ -107,6 +112,15 @@ public void enrichPlanUpdate(PlanRequest body) {
}
});
+ // Generate uuid for new additionalFields
+ if(!CollectionUtils.isEmpty(body.getPlan().getAdditionalFields())) {
+ body.getPlan().getAdditionalFields().forEach(additionalFields -> {
+ if(ObjectUtils.isEmpty(additionalFields.getId())) {
+ UUIDEnrichmentUtil.enrichRandomUuid(additionalFields, "id");
+ }
+ });
+ }
+
// Enriching last modified time for update
body.getPlan().getAuditDetails().setLastModifiedTime(System.currentTimeMillis());
}
@@ -228,4 +242,45 @@ private List getBoundaryCodeFromAncestralPath(String boundaryAncestralPa
}
return Arrays.asList(boundaryAncestralPath.split("\\|"));
}
+
+ /**
+ * Enriches the PlanSearchRequest by populating the filters map from the fields in search criteria.
+ * This filterMap is populated to search the fields in plan additional detail object.
+ *
+ * @param planSearchRequest the planSearchRequest object whose search criteria need enrichment.
+ */
+ public void enrichSearchRequest(PlanSearchRequest planSearchRequest) {
+ PlanSearchCriteria planSearchCriteria = planSearchRequest.getPlanSearchCriteria();
+
+ // Filter map for filtering plan metadata present in additional details
+ Map> filtersMap = new LinkedHashMap<>();
+
+ // Add facility id as a filter if present in search criteria
+ if (!ObjectUtils.isEmpty(planSearchCriteria.getFacilityIds())) {
+ filtersMap.put(FACILITY_ID_SEARCH_PARAMETER_KEY, planSearchCriteria.getFacilityIds());
+ }
+
+ // Add terrain as a filter if present in search criteria
+ if (!ObjectUtils.isEmpty(planSearchCriteria.getTerrain())) {
+ filtersMap.put(TERRAIN_CONDITION_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getTerrain()));
+ }
+
+ // Add onRoadCondition as a filter if present in search criteria
+ if (!ObjectUtils.isEmpty(planSearchCriteria.getOnRoadCondition())) {
+ filtersMap.put(ROAD_CONDITION_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getOnRoadCondition()));
+ }
+
+ // Add securityQ1 as a filter if present in search criteria
+ if (!ObjectUtils.isEmpty(planSearchCriteria.getSecurityQ1())) {
+ filtersMap.put(SECURITY_Q1_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getSecurityQ1()));
+ }
+
+ // Add securityQ2 as a filter if present in search criteria
+ if (!ObjectUtils.isEmpty(planSearchCriteria.getSecurityQ2())) {
+ filtersMap.put(SECURITY_Q2_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getSecurityQ2()));
+ }
+
+ if(!CollectionUtils.isEmpty(filtersMap))
+ planSearchCriteria.setFiltersMap(filtersMap);
+ }
}
diff --git a/health-services/plan-service/src/main/java/digit/service/PlanService.java b/health-services/plan-service/src/main/java/digit/service/PlanService.java
index dad7e8b7d83..d4a4416ed47 100644
--- a/health-services/plan-service/src/main/java/digit/service/PlanService.java
+++ b/health-services/plan-service/src/main/java/digit/service/PlanService.java
@@ -1,8 +1,6 @@
package digit.service;
import digit.repository.PlanRepository;
-import digit.service.enrichment.PlanEnricher;
-import digit.service.validator.PlanValidator;
import digit.service.workflow.WorkflowService;
import digit.web.models.*;
import org.egov.common.utils.ResponseInfoUtil;
@@ -61,6 +59,9 @@ public PlanResponse createPlan(PlanRequest body) {
* @return
*/
public PlanResponse searchPlan(PlanSearchRequest body) {
+ // Enrich search request
+ planEnricher.enrichSearchRequest(body);
+
// Delegate search request to repository
List planList = planRepository.search(body.getPlanSearchCriteria());
diff --git a/health-services/plan-service/src/main/java/digit/service/validator/PlanValidator.java b/health-services/plan-service/src/main/java/digit/service/PlanValidator.java
similarity index 99%
rename from health-services/plan-service/src/main/java/digit/service/validator/PlanValidator.java
rename to health-services/plan-service/src/main/java/digit/service/PlanValidator.java
index 1fd98c823b7..aa1656c00b3 100644
--- a/health-services/plan-service/src/main/java/digit/service/validator/PlanValidator.java
+++ b/health-services/plan-service/src/main/java/digit/service/PlanValidator.java
@@ -1,11 +1,9 @@
-package digit.service.validator;
+package digit.service;
import com.jayway.jsonpath.JsonPath;
import digit.config.Configuration;
import digit.repository.PlanConfigurationRepository;
import digit.repository.PlanRepository;
-import digit.service.PlanEmployeeService;
-import digit.service.enrichment.PlanEnricher;
import digit.util.BoundaryUtil;
import digit.util.CampaignUtil;
import digit.util.CommonUtil;
diff --git a/health-services/plan-service/src/main/java/digit/service/enrichment/PlanFacilityEnricher.java b/health-services/plan-service/src/main/java/digit/service/enrichment/PlanFacilityEnricher.java
index a07530dbfd2..fccb2de96df 100644
--- a/health-services/plan-service/src/main/java/digit/service/enrichment/PlanFacilityEnricher.java
+++ b/health-services/plan-service/src/main/java/digit/service/enrichment/PlanFacilityEnricher.java
@@ -3,10 +3,7 @@
import digit.util.BoundaryUtil;
import digit.util.CensusUtil;
import digit.util.CommonUtil;
-import digit.web.models.PlanFacility;
-import digit.web.models.PlanFacilityRequest;
-import digit.web.models.PlanFacilitySearchCriteria;
-import digit.web.models.PlanFacilitySearchRequest;
+import digit.web.models.*;
import digit.web.models.boundary.BoundarySearchResponse;
import digit.web.models.boundary.EnrichedBoundary;
import digit.web.models.census.*;
diff --git a/health-services/plan-service/src/main/java/digit/service/validator/PlanEmployeeAssignmentValidator.java b/health-services/plan-service/src/main/java/digit/service/validator/PlanEmployeeAssignmentValidator.java
index 772ec6dd133..774ff753132 100644
--- a/health-services/plan-service/src/main/java/digit/service/validator/PlanEmployeeAssignmentValidator.java
+++ b/health-services/plan-service/src/main/java/digit/service/validator/PlanEmployeeAssignmentValidator.java
@@ -317,8 +317,9 @@ public void validateUpdate(PlanEmployeeAssignmentRequest request) {
// Validate if Plan employee assignment exists
validatePlanEmployeeAssignmentExistance(planEmployeeAssignment);
- // Validate campaign id and employee jurisdiction
- validateCampaignDetails(planConfigurations.get(0).getCampaignId(), rootTenantId, request);
+ // Validate campaign id and employee jurisdiction for active records
+ if(planEmployeeAssignment.getActive())
+ validateCampaignDetails(planConfigurations.get(0).getCampaignId(), rootTenantId, request);
}
diff --git a/health-services/plan-service/src/main/java/digit/service/validator/PlanFacilityValidator.java b/health-services/plan-service/src/main/java/digit/service/validator/PlanFacilityValidator.java
index dd7b251eaf1..d8114403d30 100644
--- a/health-services/plan-service/src/main/java/digit/service/validator/PlanFacilityValidator.java
+++ b/health-services/plan-service/src/main/java/digit/service/validator/PlanFacilityValidator.java
@@ -126,27 +126,45 @@ private void validateCampaignDetails(String campaignId, String rootTenantId, Pla
String lowestHierarchy = hierarchyMap.get(LOWEST_HIERARCHY_FIELD_FOR_MICROPLAN);
// Collect all boundary code for the campaign
- Set boundaryCodes = fetchBoundaryCodes(campaignResponse.getCampaignDetails().get(0), lowestHierarchy);
+ Set lowestHierarchyBCodes = fetchBoundaryCodes(campaignResponse.getCampaignDetails().get(0), lowestHierarchy);
+ Set allBoundaryCodes = fetchAllBoundaryCodes(campaignResponse.getCampaignDetails().get(0));
- // Validate residing boundaries
- validateResidingBoundaries(boundaryCodes, planFacility);
+ // Validate residing boundaries against boundary codes across all hierarchy levels,
+ // as a facility's residing boundary may correspond to any jurisdiction level.
+ validateResidingBoundaries(allBoundaryCodes, planFacility);
- // Validate service boundaries
- validateServiceBoundaries(boundaryCodes, planFacility);
+ // Validate service boundaries against the lowest hierarchy boundary codes,
+ // as a facility can only be mapped to boundaries at the lowest hierarchy level.
+ validateServiceBoundaries(lowestHierarchyBCodes, planFacility);
//Enrich jurisdiction mapping and boundary ancestral path
enrichment.enrichJurisdictionMapping(planFacilityRequest, campaignResponse.getCampaignDetails().get(0).getHierarchyType());
}
/**
- * This method returns boundary code for the campaign
+ * This method returns a set of all boundary codes for the given campaign.
*
- * @param campaignDetail
- * @param lowestHierarchy
+ * @param campaignDetail the campaign details whose BCodes are required.
+ * @return returns a set of boundaries for the given campaign.
*/
- private Set fetchBoundaryCodes(CampaignDetail campaignDetail, String lowestHierarchy) {
+ private Set fetchAllBoundaryCodes(CampaignDetail campaignDetail) {
Set boundaryCodes = campaignDetail.getBoundaries().stream()
- .filter(boundary -> lowestHierarchy.equals(boundary.getType().toLowerCase()))
+ .map(Boundary::getCode)
+ .collect(Collectors.toSet());
+
+ return boundaryCodes;
+ }
+
+ /**
+ * This method filters the boundaries based on given hierarchy type for the campaign and returns a set of those boundaries.
+ *
+ * @param campaignDetail the campaign details whose BCodes are required.
+ * @param hierarchyType hierarchy type of the required boundaries.
+ * @return returns a set of boundaries of the given hierarchy type.
+ */
+ private Set fetchBoundaryCodes(CampaignDetail campaignDetail, String hierarchyType) {
+ Set boundaryCodes = campaignDetail.getBoundaries().stream()
+ .filter(boundary -> hierarchyType.equals(boundary.getType().toLowerCase()))
.map(Boundary::getCode)
.collect(Collectors.toSet());
diff --git a/health-services/plan-service/src/main/java/digit/util/QueryUtil.java b/health-services/plan-service/src/main/java/digit/util/QueryUtil.java
index 070f442a897..127dcd951a6 100644
--- a/health-services/plan-service/src/main/java/digit/util/QueryUtil.java
+++ b/health-services/plan-service/src/main/java/digit/util/QueryUtil.java
@@ -10,14 +10,10 @@
import org.springframework.util.ObjectUtils;
import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.stream.IntStream;
-import static digit.config.ServiceConstants.DOT_REGEX;
-import static digit.config.ServiceConstants.DOT_SEPARATOR;
+import static digit.config.ServiceConstants.*;
@Component
public class QueryUtil {
@@ -99,6 +95,8 @@ public String addOrderByClause(String query, String orderByClause) {
/**
* This method prepares partial json string from the filter map to query on jsonb column
*
+ * - For nested keys (keys containing dot separators), a nested JSON structure is constructed.
+ * - For other keys, a simple key-value JSON structure is returned.
* @param filterMap
* @return
*/
@@ -121,6 +119,63 @@ public String preparePartialJsonStringFromFilterMap(Map filterMa
return partialJsonQueryString;
}
+ /**
+ * This is an overloaded function, to handle facility multi select for plan search specifically.
+ * Prepares a partial JSON query string based on the provided filter map, key, and prepared statement list.
+ *
+ * @param filterMap A map containing filter keys and their corresponding set of values.
+ * @param preparedStmtList A list to which placeholder values for prepared statements are added.
+ * @param key The key used to construct the partial query.
+ * @return A partial JSON query string or a query fragment with OR conditions.
+ *
+ * This method dynamically constructs a JSON query or query fragment based on the structure of the `key`:
+ * - For nested keys (keys containing dot separators), a nested JSON structure is constructed.
+ * - For the "facilityId" key, an OR condition is generated for all values in the set.
+ * - For other keys, a simple key-value JSON structure is returned.
+ *
+ * Notes:
+ * - When constructing the query for "facilityId", placeholders for each value are added to the `preparedStmtList`.
+ */
+ public String preparePartialJsonStringFromFilterMap(Map> filterMap, List preparedStmtList, String key) {
+ Map queryMap = new HashMap<>();
+ StringBuilder finalJsonQuery = new StringBuilder();
+ // Handle nested keys (dot separator)
+ if (key.contains(DOT_SEPARATOR)) {
+ String[] keyArray = key.split(DOT_REGEX);
+ Map nestedQueryMap = new HashMap<>();
+ prepareNestedQueryMap(0, keyArray, nestedQueryMap, (String) filterMap.get(key).toArray()[0]);
+ queryMap.put(keyArray[0], nestedQueryMap.get(keyArray[0]));
+ return gson.toJson(queryMap);
+ } else if (FACILITY_ID_SEARCH_PARAMETER_KEY.equals(key)) {
+ Set values = filterMap.get(key);
+ if (values != null && !values.isEmpty()) {
+ StringBuilder orClauseBuilder = new StringBuilder();
+
+ // For each value, add an OR condition with a placeholder for the value
+ for (String value : values) {
+ if (orClauseBuilder.length() > 0) {
+ orClauseBuilder.append(OR_CONDITION);
+ }
+ // Add the condition for the key-value pair
+ orClauseBuilder.append(JSONB_QUERY_FORMAT);
+ // Add the actual value in the format { "facilityId": "value" }
+ preparedStmtList.add("{\"" + key + "\": \"" + value + "\"}");
+ }
+
+ // Append the OR clause as part of the AND conditions
+ finalJsonQuery.append(AND_CONDITION).append("(").append(orClauseBuilder.toString()).append(") ");
+ return finalJsonQuery.toString(); // Return the query with OR conditions for facilityId
+ }
+ } else {
+ queryMap.put(key, (String) filterMap.get(key).toArray()[0]);
+ }
+
+
+ // Return the dynamically constructed query string
+ return gson.toJson(queryMap);
+ }
+
+
/**
* Tail recursive method to prepare n-level nested partial json for queries on nested data in
* master data. For e.g. , if the key is in the format a.b.c, it will construct a nested json
diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/AdditionalField.java b/health-services/plan-service/src/main/java/digit/web/models/AdditionalField.java
similarity index 96%
rename from health-services/plan-service/src/main/java/digit/web/models/census/AdditionalField.java
rename to health-services/plan-service/src/main/java/digit/web/models/AdditionalField.java
index 65ae4222831..10351283cdb 100644
--- a/health-services/plan-service/src/main/java/digit/web/models/census/AdditionalField.java
+++ b/health-services/plan-service/src/main/java/digit/web/models/AdditionalField.java
@@ -1,4 +1,4 @@
-package digit.web.models.census;
+package digit.web.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
diff --git a/health-services/plan-service/src/main/java/digit/web/models/Plan.java b/health-services/plan-service/src/main/java/digit/web/models/Plan.java
index 3ff0e8ce87d..f39dcfa6767 100644
--- a/health-services/plan-service/src/main/java/digit/web/models/Plan.java
+++ b/health-services/plan-service/src/main/java/digit/web/models/Plan.java
@@ -74,6 +74,10 @@ public class Plan {
@JsonProperty("jurisdictionMapping")
private Map jurisdictionMapping;
+ @JsonProperty("additionalFields")
+ @Valid
+ private List additionalFields = null;
+
@JsonIgnore
private String boundaryAncestralPath = null;
diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanDTO.java b/health-services/plan-service/src/main/java/digit/web/models/PlanDTO.java
index f56569c1349..d7adb85ece0 100644
--- a/health-services/plan-service/src/main/java/digit/web/models/PlanDTO.java
+++ b/health-services/plan-service/src/main/java/digit/web/models/PlanDTO.java
@@ -59,6 +59,10 @@ public class PlanDTO {
@JsonProperty("jurisdictionMapping")
private Map jurisdictionMapping;
+ @JsonProperty("additionalFields")
+ @Valid
+ private List additionalFields = null;
+
@JsonProperty("activities")
@Valid
private List activities;
diff --git a/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java b/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java
index 357b306fa8f..f0b22e87d65 100644
--- a/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java
+++ b/health-services/plan-service/src/main/java/digit/web/models/PlanSearchCriteria.java
@@ -1,5 +1,6 @@
package digit.web.models;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
@@ -10,6 +11,7 @@
import org.springframework.validation.annotation.Validated;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -38,6 +40,21 @@ public class PlanSearchCriteria {
@JsonProperty("planConfigurationId")
private String planConfigurationId = null;
+ @JsonProperty("facilityIds")
+ private Set facilityIds = null;
+
+ @JsonProperty("onRoadCondition")
+ private String onRoadCondition = null;
+
+ @JsonProperty("terrain")
+ private String terrain = null;
+
+ @JsonProperty("securityQ1")
+ private String securityQ1 = null;
+
+ @JsonProperty("securityQ2")
+ private String securityQ2 = null;
+
@JsonProperty("status")
private String status = null;
@@ -54,4 +71,7 @@ public class PlanSearchCriteria {
@JsonProperty("limit")
private Integer limit = null;
+ @JsonIgnore
+ private Map> filtersMap = null;
+
}
diff --git a/health-services/plan-service/src/main/java/digit/web/models/Resource.java b/health-services/plan-service/src/main/java/digit/web/models/Resource.java
index d0d8ce2500b..2a614db0ba6 100644
--- a/health-services/plan-service/src/main/java/digit/web/models/Resource.java
+++ b/health-services/plan-service/src/main/java/digit/web/models/Resource.java
@@ -29,7 +29,6 @@ public class Resource {
private String resourceType = null;
@JsonProperty("estimatedNumber")
- @NotNull
private BigDecimal estimatedNumber = null;
@JsonProperty("activityCode")
diff --git a/health-services/plan-service/src/main/java/digit/web/models/census/Census.java b/health-services/plan-service/src/main/java/digit/web/models/census/Census.java
index e8f384d1e98..ad052ca93ce 100644
--- a/health-services/plan-service/src/main/java/digit/web/models/census/Census.java
+++ b/health-services/plan-service/src/main/java/digit/web/models/census/Census.java
@@ -8,6 +8,7 @@
import java.util.List;
+import digit.web.models.AdditionalField;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
@@ -48,8 +49,7 @@ public class Census {
private String boundaryCode = null;
@JsonProperty("assignee")
- @Size(max = 64)
- private String assignee = null;
+ private List assignee = null;
@JsonProperty("status")
@Size(max = 64)
diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20242112151500__plan_additional_field_create_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20242112151500__plan_additional_field_create_ddl.sql
new file mode 100644
index 00000000000..764ee79bb2b
--- /dev/null
+++ b/health-services/plan-service/src/main/resources/db/migration/main/V20242112151500__plan_additional_field_create_ddl.sql
@@ -0,0 +1,12 @@
+-- Table: plan_additional_field
+CREATE TABLE plan_additional_field (
+ id character varying(64) NOT NULL,
+ plan_id character varying(64) NOT NULL,
+ "key" character varying(64) NOT NULL,
+ "value" numeric(12,2) NOT NULL,
+ show_on_ui boolean DEFAULT true NOT NULL,
+ editable boolean DEFAULT true NOT NULL,
+ "order" bigint NOT NULL,
+ CONSTRAINT uk_plan_additional_field_id PRIMARY KEY (id),
+ FOREIGN KEY (plan_id) REFERENCES plan(id)
+);
diff --git a/health-services/plan-service/src/main/resources/db/migration/main/V20242201141000__alter_plan_facility_linkage_facility_name_ddl.sql b/health-services/plan-service/src/main/resources/db/migration/main/V20242201141000__alter_plan_facility_linkage_facility_name_ddl.sql
new file mode 100644
index 00000000000..649a80d10f3
--- /dev/null
+++ b/health-services/plan-service/src/main/resources/db/migration/main/V20242201141000__alter_plan_facility_linkage_facility_name_ddl.sql
@@ -0,0 +1 @@
+ALTER TABLE plan_facility_linkage ALTER COLUMN facility_name TYPE character varying(2000);
diff --git a/health-services/resource-generator/CHANGELOG.md b/health-services/resource-generator/CHANGELOG.md
index f56811de913..fcc5d765bde 100644
--- a/health-services/resource-generator/CHANGELOG.md
+++ b/health-services/resource-generator/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## 1.0.1 - 2025-01-30
+1. Enhancements for Microplan Estimation Downloadable Excel Report
+2. Enabled support for Mixed Distribution Strategy for Registration and Service Delivery
+3. Changes for supporting filters in Estimation Dashboard
+
## 1.0.0 - 2024-12-03
#### Resource Generator Service
The Resource Generator Service introduces comprehensive functionalities for microplanning resource estimation and campaign integration:
diff --git a/health-services/resource-generator/pom.xml b/health-services/resource-generator/pom.xml
index 33ac3c4c7d6..f194f1e0e32 100644
--- a/health-services/resource-generator/pom.xml
+++ b/health-services/resource-generator/pom.xml
@@ -4,7 +4,7 @@
resource-generator
jar
file-processor-utility
- 1.0.0
+ 1.0.1
17
32-SNAPSHOT
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/config/Configuration.java b/health-services/resource-generator/src/main/java/org/egov/processor/config/Configuration.java
index c652337ba28..c0e08a02aa2 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/config/Configuration.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/config/Configuration.java
@@ -41,6 +41,9 @@ public class Configuration {
@Value("${egov.plan.search.endpoint}")
private String planSearchEndPoint;
+ @Value("${egov.plan.facility.search.endpoint}")
+ private String planFacilitySearchEndPoint;
+
// Filestore
@Value("${egov.filestore.service.host}")
@@ -109,11 +112,11 @@ public class Configuration {
private String resourceCensusCreateTopic;
//Default
- @Value("${resource.default.offset}")
- private Integer defaultOffset;
+ @Value("${default.offset.for.mdms.data}")
+ private Integer defaultOffsetForMdms;
- @Value("${resource.default.limit}")
- private Integer defaultLimit;
+ @Value("${default.limit.for.mdms.data}")
+ private Integer defaultLimitForMdms;
//census additonal field configs
@Value("${census.additional.field.override.keys}")
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/config/ServiceConstants.java b/health-services/resource-generator/src/main/java/org/egov/processor/config/ServiceConstants.java
index 7200f29cecc..ae2e04160d6 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/config/ServiceConstants.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/config/ServiceConstants.java
@@ -28,17 +28,27 @@ public class ServiceConstants {
public static final String MICROPLANNING_MODULE = "microplan";
+ //Custom Exceptions
public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE = "NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT";
public static final String NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE = "Invalid or incorrect TenantId. No mdms data found for provided Tenant.";
public static final String ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE = "Exception occurred while fetching plan configuration from plan service ";
+ public static final String ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_CODE = "ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS";
+ public static final String ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_MESSAGE = "Exception occurred while updating additional details : ";
+
public static final String NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM_CODE = "NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM";
public static final String NOT_ABLE_TO_CONVERT_MULTIPARTFILE_TO_BYTESTREAM_MESSAGE = "Not able to fetch byte stream from a multipart file";
public static final String FILE_NOT_FOUND_CODE = "FILE_NOT_FOUND";
public static final String FILE_NOT_FOUND_MESSAGE = "No file with the specified templateIdentifier found - ";
+ public static final String PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_CODE = "PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT";
+ public static final String PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_MESSAGE = "Key is not present in json object - ";
+
+ public static final String EMPTY_HEADER_ROW_CODE = "EMPTY_HEADER_ROW";
+ public static final String EMPTY_HEADER_ROW_MESSAGE = "The header row is empty for the given sheet";
+
public static final String UNABLE_TO_CREATE_ADDITIONAL_DETAILS_CODE = "UNABLE_TO_CREATE_ADDITIONAL_DETAILS";
public static final String UNABLE_TO_CREATE_ADDITIONAL_DETAILS_MESSAGE = "Unable to create additional details for facility creation.";
@@ -48,32 +58,58 @@ public class ServiceConstants {
public static final String NO_PLAN_FOUND_FOR_GIVEN_DETAILS_CODE = "NO_PLAN_FOUND_FOR_GIVEN_DETAILS";
public static final String NO_PLAN_FOUND_FOR_GIVEN_DETAILS_MESSAGE = "Plan records do not exists for the given details: ";
+ public static final String NO_PLAN_FACILITY_FOUND_FOR_GIVEN_DETAILS_CODE = "NO_PLAN_FACILITY_FOUND_FOR_GIVEN_DETAILS";
+ public static final String NO_PLAN_FACILITY_FOUND_FOR_GIVEN_DETAILS_MESSAGE = "Plan facilities do not exists for the given details. ";
+
+ public static final String README_SHEET_NAME_LOCALISATION_NOT_FOUND_CODE = "README_SHEET_NAME_LOCALISATION_NOT_FOUND";
+ public static final String README_SHEET_NAME_LOCALISATION_NOT_FOUND_MESSAGE = "Constant defined for error message when the README sheet name localization is not found or plan facilities do not exist for the provided details.";
+
+ public static final String NO_MDMS_DATA_FOUND_FOR_MIXED_STRATEGY_MASTER_CODE = "NO_MDMS_DATA_FOUND_FOR_MIXED_STRATEGY_MASTER";
+ public static final String NO_MDMS_DATA_FOUND_FOR_MIXED_STRATEGY_MASTER_CODE_MESSAGE = "Master data not found for Mixed Strategy master";
+
public static final String BOUNDARY_CODE = "HCM_ADMIN_CONSOLE_BOUNDARY_CODE";
public static final String TOTAL_POPULATION = "HCM_ADMIN_CONSOLE_TOTAL_POPULATION";
+ public static final String ERROR_PROCESSING_DATA_FROM_MDMS = "Exception occurred while processing data from mdms ";
public static final String ERROR_WHILE_FETCHING_FROM_PLAN_SERVICE_FOR_LOCALITY = "Exception occurred while fetching plan configuration from plan service for Locality ";
public static final String ERROR_WHILE_PUSHING_TO_PLAN_SERVICE_FOR_LOCALITY = "Exception occurred while fetching plan configuration from plan service for Locality ";
public static final String ERROR_WHILE_SEARCHING_CAMPAIGN = "Exception occurred while searching/updating campaign.";
public static final String ERROR_WHILE_DATA_CREATE_CALL = "Exception occurred while creating data for campaign - ";
public static final String ERROR_WHILE_CALLING_MICROPLAN_API =
"Unexpected error while calling fetch from Microplan API for plan config Id: ";
+ public static final String INVALID_HEX = "Invalid hex color specified: ";
+
+ public static final String DISTRIBUTION_PROCESS = "DistributionProcess";
+ public static final String REGISTRATION_PROCESS = "RegistrationProcess";
+ //File constants
public static final String FILE_NAME = "output.xls";
public static final String FILE_TYPE = "boundaryWithTarget";
public static final String FILE_TEMPLATE_IDENTIFIER_POPULATION = "Population";
+ public static final String FILE_TEMPLATE_IDENTIFIER_BOUNDARY = "boundaryWithTarget";
public static final String FILE_TEMPLATE_IDENTIFIER_FACILITY = "Facilities";
public static final String INPUT_IS_NOT_VALID = "File does not contain valid input for row ";
-
+
+ //Mdms constants and masters
public static final String MDMS_SCHEMA_TYPE = "type";
public static final String MDMS_SCHEMA_SECTION = "section";
+ public static final String MDMS_SCHEMA_TITLE = "title";
public static final String MDMS_PLAN_MODULE_NAME = "hcm-microplanning";
public static final String MDMS_MASTER_SCHEMAS = "Schemas";
+ public static final String MDMS_MASTER_ADMIN_SCHEMA = "adminSchema";
public static final String MDMS_CAMPAIGN_TYPE = "campaignType";
public static final String MDMS_SCHEMA_ADMIN_SCHEMA = "adminSchema";
+ public static final String MDMS_MASTER_MIXED_STRATEGY = "MixedStrategyOperationLogic";
public static final String MDMS_ADMIN_CONSOLE_MODULE_NAME = "HCM-ADMIN-CONSOLE";
public static final String BOUNDARY = "boundary";
public static final String DOT_SEPARATOR = ".";
public static final String MICROPLAN_PREFIX = "MP-";
+ public static final Double BRIGHTEN_FACTOR = 1.1;
+ public static final String ACCESSIBILITY_DETAILS = "accessibilityDetails";
+ public static final String SECURITY_DETAILS = "securityDetails";
+ public static final String EMPTY_STRING = "";
+ public static final String NOT_APPLICABLE = "N/A";
+ public static final String FIXED_POST_YES = "yes";
//MDMS field Constants
public static final String DATA = "data";
@@ -82,8 +118,11 @@ public class ServiceConstants {
public static final String STRING_PROPERTIES = "stringProperties";
public static final String NAME = "name";
+ //Error messages
public static final String ERROR_WHILE_UPDATING_PLAN_CONFIG = "Exception occurred while updating plan configuration.";
- public static final String ERROR_WHILE_SEARCHING_PLAN = "Exception occurred while search plans.";
+ public static final String ERROR_WHILE_SEARCHING_PLAN = "Exception occurred while searching plans.";
+ public static final String ERROR_WHILE_SEARCHING_PLAN_FACILITY = "Exception occurred while searching plan facility : ";
+ public static final String ERROR_WHILE_CREATING_CELL_STYLE = "Failed to create cell style : ";
public static final String VALIDATE_STRING_REGX = "^(?!\\d+$).+$";
public static final String VALIDATE_NUMBER_REGX = "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$";
@@ -112,6 +151,10 @@ public class ServiceConstants {
//Facility Create constants
public static final String TYPE_FACILITY = "facility";
+ public static final String FACILITY_NAME = "facilityName";
+ public static final String FACILITY_ID = "facilityId";
+ public static final String HCM_MICROPLAN_SERVING_FACILITY = "HCM_MICROPLAN_SERVING_FACILITY";
+ public static final String FIXED_POST = "fixedPost";
public static final String ACTION_CREATE = "create";
public static final String SOURCE_KEY = "source";
public static final String MICROPLAN_SOURCE_KEY = "microplan";
@@ -120,4 +163,11 @@ public class ServiceConstants {
//Census additional field constants
public static final String UPLOADED_KEY = "UPLOADED_";
public static final String CONFIRMED_KEY = "CONFIRMED_";
+ public static final String CODE = "code";
+
+ //Excel header row styling constants
+ public static final String HEX_BACKGROUND_COLOR = "93C47D"; // Background color in HEX format (RRGGBB) for Excel header rows
+ public static final boolean FREEZE_CELL = true; // Controls whether cells should be locked for editing
+ public static final int COLUMN_WIDTH = 40; //Default column width in characters (1-255)
+ public static final int COLUMN_PADDING = 512;
}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/service/ExcelParser.java b/health-services/resource-generator/src/main/java/org/egov/processor/service/ExcelParser.java
index 6f2326d628a..56a4e6de9ce 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/service/ExcelParser.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/service/ExcelParser.java
@@ -13,15 +13,21 @@
import org.egov.processor.config.ServiceConstants;
import org.egov.processor.util.*;
import org.egov.processor.web.models.*;
-import org.egov.processor.web.models.Locale;
import org.egov.processor.web.models.boundary.BoundarySearchResponse;
import org.egov.processor.web.models.boundary.EnrichedBoundary;
import org.egov.processor.web.models.campaignManager.Boundary;
import org.egov.processor.web.models.campaignManager.CampaignResources;
import org.egov.processor.web.models.campaignManager.CampaignResponse;
+import org.egov.processor.web.models.mdmsV2.MixedStrategyOperationLogic;
+import org.egov.processor.web.models.planFacility.PlanFacility;
+import org.egov.processor.web.models.planFacility.PlanFacilityResponse;
+import org.egov.processor.web.models.planFacility.PlanFacilitySearchCriteria;
+import org.egov.processor.web.models.planFacility.PlanFacilitySearchRequest;
import org.egov.tracer.model.CustomException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
import java.io.File;
import java.io.FileNotFoundException;
@@ -31,8 +37,7 @@
import java.util.*;
import java.util.stream.Collectors;
-import static org.egov.processor.config.ServiceConstants.HCM_ADMIN_CONSOLE_BOUNDARY_DATA;
-import static org.egov.processor.config.ServiceConstants.READ_ME_SHEET_NAME;
+import static org.egov.processor.config.ServiceConstants.*;
@Slf4j
@Service
@@ -64,9 +69,15 @@ public class ExcelParser implements FileParser {
private PlanConfigurationUtil planConfigurationUtil;
+ private OutputEstimationGenerationUtil outputEstimationGenerationUtil;
+
+ private MixedStrategyUtil mixedStrategyUtil;
+
+ private PlanFacilityUtil planFacilityUtil;
+
public ExcelParser(ObjectMapper objectMapper, ParsingUtil parsingUtil, FilestoreUtil filestoreUtil,
- CalculationUtil calculationUtil, PlanUtil planUtil, CampaignIntegrationUtil campaignIntegrationUtil,
- Configuration config, MdmsUtil mdmsUtil, BoundaryUtil boundaryUtil, LocaleUtil localeUtil, CensusUtil censusUtil, EnrichmentUtil enrichmentUtil, PlanConfigurationUtil planConfigurationUtil) {
+ CalculationUtil calculationUtil, PlanUtil planUtil, CampaignIntegrationUtil campaignIntegrationUtil,
+ Configuration config, MdmsUtil mdmsUtil, BoundaryUtil boundaryUtil, LocaleUtil localeUtil, CensusUtil censusUtil, EnrichmentUtil enrichmentUtil, PlanConfigurationUtil planConfigurationUtil, OutputEstimationGenerationUtil outputEstimationGenerationUtil, MixedStrategyUtil mixedStrategyUtil, PlanFacilityUtil planFacilityUtil) {
this.objectMapper = objectMapper;
this.parsingUtil = parsingUtil;
this.filestoreUtil = filestoreUtil;
@@ -80,6 +91,9 @@ public ExcelParser(ObjectMapper objectMapper, ParsingUtil parsingUtil, Filestore
this.censusUtil = censusUtil;
this.enrichmentUtil = enrichmentUtil;
this.planConfigurationUtil = planConfigurationUtil;
+ this.outputEstimationGenerationUtil = outputEstimationGenerationUtil;
+ this.mixedStrategyUtil = mixedStrategyUtil;
+ this.planFacilityUtil = planFacilityUtil;
}
/**
@@ -123,12 +137,10 @@ private void processExcelFile(PlanConfigurationRequest planConfigurationRequest,
Object campaignResponse) {
try (Workbook workbook = new XSSFWorkbook(file)) {
List campaignBoundaryList = new ArrayList<>();
- List campaignResourcesList = new ArrayList<>();
DataFormatter dataFormatter = new DataFormatter();
processSheets(planConfigurationRequest, fileStoreId, campaignResponse, workbook,
campaignBoundaryList, dataFormatter);
- uploadFileAndIntegrateCampaign(planConfigurationRequest, campaignResponse,
- workbook, campaignBoundaryList, campaignResourcesList);
+ uploadFileAndIntegrateCampaign(planConfigurationRequest, workbook, fileStoreId);
} catch (FileNotFoundException e) {
log.error("File not found: {}", e.getMessage());
throw new CustomException("FileNotFound", "The specified file was not found.");
@@ -144,16 +156,12 @@ private void processExcelFile(PlanConfigurationRequest planConfigurationRequest,
/**
* Uploads a converted file and integrates campaign details if configured to do so.
- *
+ *
* @param planConfigurationRequest The request containing configuration details including tenant ID.
- * @param campaignResponse The response object containing campaign details.
- * @param workbook The workbook containing data to be uploaded and integrated.
- * @param campaignBoundaryList List of boundary objects related to the campaign.
- * @param campaignResourcesList List of campaign resources to be integrated.
+ * @param workbook The workbook containing data to be uploaded and integrated.
+ * @param filestoreId The ID of the file in the file store.
*/
- private void uploadFileAndIntegrateCampaign(PlanConfigurationRequest planConfigurationRequest,
- Object campaignResponse, Workbook workbook,
- List campaignBoundaryList, List campaignResourcesList) {
+ private void uploadFileAndIntegrateCampaign(PlanConfigurationRequest planConfigurationRequest, Workbook workbook, String filestoreId) {
File fileToUpload = null;
try {
PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration();
@@ -164,9 +172,19 @@ private void uploadFileAndIntegrateCampaign(PlanConfigurationRequest planConfigu
planUtil.update(planConfigurationRequest);
}
if (planConfig.getStatus().equals(config.getPlanConfigUpdatePlanEstimatesIntoOutputFileStatus()) && config.isIntegrateWithAdminConsole()) {
+ //Upload the processed output file into project factory
String uploadedFileStoreId = uploadConvertedFile(fileToUpload, planConfig.getTenantId());
campaignIntegrationUtil.updateResourcesInProjectFactory(planConfigurationRequest, uploadedFileStoreId);
- }
+
+ //process output file for localized header columns and addition of new columns
+ outputEstimationGenerationUtil.processOutputFile(workbook, planConfigurationRequest, filestoreId);
+
+ //upload the processed output file and update the same into plan configuration file object
+ fileToUpload = convertWorkbookToXls(workbook);
+ uploadedFileStoreId = uploadConvertedFile(fileToUpload, planConfig.getTenantId());
+ planUtil.setFileStoreIdForPopulationTemplate(planConfigurationRequest, uploadedFileStoreId);
+ planUtil.update(planConfigurationRequest);
+ }
} finally {
try {
if (fileToUpload != null && !fileToUpload.delete()) {
@@ -189,7 +207,6 @@ private void uploadFileAndIntegrateCampaign(PlanConfigurationRequest planConfigu
* @param campaignBoundaryList List of boundary objects related to the campaign.
* @param dataFormatter The data formatter for formatting cell values.
*/
-
private void processSheets(PlanConfigurationRequest request, String fileStoreId,
Object campaignResponse, Workbook excelWorkbook,
List campaignBoundaryList,
@@ -212,12 +229,20 @@ private void processSheets(PlanConfigurationRequest request, String fileStoreId,
(existing, replacement) -> existing,
LinkedHashMap::new
));
+
+ // Fetch mdms data for common constants
+ Map mdmsDataForCommonConstants = mdmsUtil.fetchMdmsDataForCommonConstants(
+ request.getRequestInfo(),
+ request.getPlanConfiguration().getTenantId());
+
excelWorkbook.forEach(excelWorkbookSheet -> {
- if (isSheetAllowedToProcess(request, excelWorkbookSheet.getSheetName(), localeResponse)) {
+ if (outputEstimationGenerationUtil.isSheetAllowedToProcess(excelWorkbookSheet.getSheetName(), localeResponse, mdmsDataForCommonConstants)) {
if (request.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerPlanEstimatesStatus())) {
- enrichmentUtil.enrichsheetWithApprovedCensusRecords(excelWorkbookSheet, request, fileStoreId, mappedValues);
+ Map boundaryCodeToCensusAdditionalDetails = new HashMap<>();
+
+ enrichmentUtil.enrichsheetWithApprovedCensusRecords(excelWorkbookSheet, request, fileStoreId, mappedValues, boundaryCodeToCensusAdditionalDetails);
processRows(request, excelWorkbookSheet, dataFormatter, fileStoreId,
- campaignBoundaryList, attributeNameVsDataTypeMap, boundaryCodeList);
+ campaignBoundaryList, attributeNameVsDataTypeMap, boundaryCodeList, boundaryCodeToCensusAdditionalDetails);
} else if (request.getPlanConfiguration().getStatus().equals(config.getPlanConfigTriggerCensusRecordsStatus())) {
processRowsForCensusRecords(request, excelWorkbookSheet,
fileStoreId, attributeNameVsDataTypeMap, boundaryCodeList, campaign.getCampaign().get(0).getHierarchyType());
@@ -228,6 +253,54 @@ private void processSheets(PlanConfigurationRequest request, String fileStoreId,
});
}
+ /**
+ * This method makes plan facility search call and creates a map of boundary code to it's fixed post facility details.
+ *
+ * @param request the plan configuration request.
+ * @param sheet the Excel sheet to be processed.
+ * @param fileStoreId the fileStore id of the file.
+ * @return returns a map of boundary code to it's fixed post facility details.
+ */
+ private Map fetchFixedPostDetails(PlanConfigurationRequest request, Sheet sheet, String fileStoreId) {
+ PlanConfiguration planConfiguration = request.getPlanConfiguration();
+
+ //Create plan facility search request
+ PlanFacilitySearchRequest searchRequest = PlanFacilitySearchRequest.builder()
+ .requestInfo(request.getRequestInfo())
+ .planFacilitySearchCriteria(PlanFacilitySearchCriteria.builder()
+ .tenantId(planConfiguration.getTenantId())
+ .planConfigurationId(planConfiguration.getId())
+ .build())
+ .build();
+
+ PlanFacilityResponse planFacilityResponse = planFacilityUtil.search(searchRequest);
+
+ if (ObjectUtils.isEmpty(planFacilityResponse) || CollectionUtils.isEmpty(planFacilityResponse.getPlanFacility())) {
+ throw new CustomException(NO_PLAN_FACILITY_FOUND_FOR_GIVEN_DETAILS_CODE, NO_PLAN_FACILITY_FOUND_FOR_GIVEN_DETAILS_MESSAGE);
+ }
+
+ // Create a Boundary Code to Facility's fixed post detail map.
+ Map boundaryCodeToFixedPostMap = new HashMap<>();
+
+ for (PlanFacility planFacility : planFacilityResponse.getPlanFacility()) {
+ // Ensure serviceBoundaries are not empty
+ if (!CollectionUtils.isEmpty(planFacility.getServiceBoundaries())) {
+
+ // Extract the 'FIXED_POST' field from additional details.
+ String fixedPostValue = (String) parsingUtil.extractFieldsFromJsonObject(planFacility.getAdditionalDetails(), FIXED_POST);
+
+ // Normalize the value and determine boolean equivalent.
+ Boolean isFixedPost = !ObjectUtils.isEmpty(fixedPostValue) && fixedPostValue.trim().equalsIgnoreCase(FIXED_POST_YES);
+
+ // Populate the map with boundary code and isFixedPost.
+ planFacility.getServiceBoundaries().forEach((String boundary) ->
+ boundaryCodeToFixedPostMap.put(boundary, isFixedPost)
+ );
+ }
+ }
+ return boundaryCodeToFixedPostMap;
+ }
+
/**
* Processes rows of data in an Excel sheet, performs calculations, updates
* campaign boundaries, and creates plans.
@@ -246,9 +319,13 @@ private void processSheets(PlanConfigurationRequest request, String fileStoreId,
* @param boundaryCodeList List of boundary codes.
* @throws IOException If an I/O error occurs.
*/
- private void processRows(PlanConfigurationRequest planConfigurationRequest, Sheet sheet, DataFormatter dataFormatter, String fileStoreId, List campaignBoundaryList, Map attributeNameVsDataTypeMap, List boundaryCodeList) {
- PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration();
- performRowLevelCalculations(planConfigurationRequest, sheet, dataFormatter, fileStoreId, campaignBoundaryList, planConfig, attributeNameVsDataTypeMap, boundaryCodeList);
+ private void processRows(PlanConfigurationRequest planConfigurationRequest, Sheet sheet, DataFormatter dataFormatter,
+ String fileStoreId, List campaignBoundaryList, Map attributeNameVsDataTypeMap,
+ List boundaryCodeList, Map boundaryCodeToCensusAdditionalDetails) {
+
+ // Create a Map of Boundary Code to Facility's fixed post detail.
+ Map boundaryCodeToFixedPostMap = fetchFixedPostDetails(planConfigurationRequest, sheet, fileStoreId);
+ performRowLevelCalculations(planConfigurationRequest, sheet, dataFormatter, fileStoreId, campaignBoundaryList, attributeNameVsDataTypeMap, boundaryCodeList, boundaryCodeToFixedPostMap, boundaryCodeToCensusAdditionalDetails);
}
private void processRowsForCensusRecords(PlanConfigurationRequest planConfigurationRequest, Sheet sheet, String fileStoreId, Map attributeNameVsDataTypeMap, List boundaryCodeList, String hierarchyType) {
@@ -312,14 +389,11 @@ private List getBoundaryCodeList(PlanConfigurationRequest planConfigurat
* @return A map of attribute names to their corresponding indices or data types.
*/
-
- //TODO: fetch from adminSchema master
private Map prepareAttributeVsIndexMap(PlanConfigurationRequest planConfigurationRequest,
String fileStoreId, CampaignResponse campaign, PlanConfiguration planConfig, Object mdmsData) {
org.egov.processor.web.models.File file = planConfig.getFiles().stream()
.filter(f -> f.getFilestoreId().equalsIgnoreCase(fileStoreId)).findFirst().get();
- return mdmsUtil.filterMasterData(mdmsData.toString(), file.getInputFileType(),
- file.getTemplateIdentifier(), campaign.getCampaign().get(0).getProjectType());
+ return mdmsUtil.filterMasterData(mdmsData.toString(), campaign.getCampaign().get(0).getProjectType());
}
@@ -333,14 +407,15 @@ private Map prepareAttributeVsIndexMap(PlanConfigurationRequest
* @param dataFormatter The data formatter for formatting cell values.
* @param fileStoreId The ID of the uploaded file in the file store.
* @param campaignBoundaryList List of boundary objects related to the campaign.
- * @param planConfig The configuration details specific to the plan.
* @param attributeNameVsDataTypeMap Mapping of attribute names to their data types.
* @param boundaryCodeList List of boundary codes.
*/
private void performRowLevelCalculations(PlanConfigurationRequest planConfigurationRequest, Sheet sheet,
DataFormatter dataFormatter, String fileStoreId, List campaignBoundaryList,
- PlanConfiguration planConfig, Map attributeNameVsDataTypeMap, List boundaryCodeList) {
+ Map attributeNameVsDataTypeMap, List boundaryCodeList,
+ Map boundaryCodeToFixedPostMap, Map boundaryCodeToCensusAdditionalDetails) {
Row firstRow = null;
+ PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration();
Map mappedValues = planConfig.getResourceMapping().stream()
.filter(f -> f.getFilestoreId().equals(fileStoreId))
.collect(Collectors.toMap(ResourceMapping::getMappedTo, ResourceMapping::getMappedFrom));
@@ -351,6 +426,9 @@ private void performRowLevelCalculations(PlanConfigurationRequest planConfigurat
Integer indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0,
parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues);
+ List mixedStrategyOperationLogicList = mixedStrategyUtil
+ .fetchMixedStrategyOperationLogicFromMDMS(planConfigurationRequest);
+
for (Row row : sheet) {
if(parsingUtil.isRowEmpty(row))
continue;
@@ -366,10 +444,11 @@ private void performRowLevelCalculations(PlanConfigurationRequest planConfigurat
JsonNode feature = createFeatureNodeFromRow(row, mapOfColumnNameAndIndex);
performCalculationsOnOperations(sheet, planConfig, row, resultMap, mappedValues,
assumptionValueMap, feature);
- if (config.isIntegrateWithAdminConsole())
- campaignIntegrationUtil.updateCampaignBoundary(planConfig, feature, assumptionValueMap, mappedValues,
- mapOfColumnNameAndIndex, campaignBoundaryList, resultMap);
- planUtil.create(planConfigurationRequest, feature, resultMap, mappedValues);
+
+ // Get Boundary Code for the current row.
+ String boundaryCode = row.getCell(indexOfBoundaryCode).getStringCellValue();
+ mixedStrategyUtil.processResultMap(resultMap, planConfig.getOperations(), mixedStrategyUtil.getCategoriesNotAllowed(boundaryCodeToFixedPostMap.get(boundaryCode), planConfig, mixedStrategyOperationLogicList));
+ planUtil.create(planConfigurationRequest, feature, resultMap, mappedValues, boundaryCodeToCensusAdditionalDetails);
}
}
@@ -400,10 +479,12 @@ private void performCalculationsOnOperations(Sheet sheet, PlanConfiguration plan
Cell cell = row.createCell(columnIndex++);
cell.setCellValue(result.doubleValue());
+ cell.getCellStyle().setLocked(false); // Ensure the new cell is editable
if (row.getRowNum() == 1) {
Cell headerCell = sheet.getRow(0).createCell(row.getLastCellNum() - 1);
headerCell.setCellValue(output);
+ headerCell.getCellStyle().setLocked(true);
}
}
@@ -717,29 +798,6 @@ public List getAllBoundaryPresentforHierarchyType(List
}
return boundaryList;
}
-
- /**
- * Checks if a sheet is allowed to be processed based on MDMS constants and locale-specific configuration.
- *
- * @param planConfigurationRequest The request containing configuration details including request info and tenant ID.
- * @param sheetName The name of the sheet to be processed.
- * @return true if the sheet is allowed to be processed, false otherwise.
- * @throws JsonMappingException If there's an issue mapping JSON response to Java objects.
- * @throws JsonProcessingException If there's an issue processing JSON during conversion.
- */
- private boolean isSheetAllowedToProcess(PlanConfigurationRequest planConfigurationRequest, String sheetName, LocaleResponse localeResponse) {
- Map mdmsDataConstants = mdmsUtil.fetchMdmsDataForCommonConstants(
- planConfigurationRequest.getRequestInfo(),
- planConfigurationRequest.getPlanConfiguration().getTenantId());
-
- for (Locale locale : localeResponse.getMessages()) {
- if ((locale.getCode().equalsIgnoreCase((String) mdmsDataConstants.get(READ_ME_SHEET_NAME)))
- || locale.getCode().equalsIgnoreCase(HCM_ADMIN_CONSOLE_BOUNDARY_DATA)) {
- if (sheetName.equals(locale.getMessage()))
- return false;
- }
- }
- return true;
- }
+
}
\ No newline at end of file
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/CalculationUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/CalculationUtil.java
index 551206d9b94..d53a749cbff 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/util/CalculationUtil.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/CalculationUtil.java
@@ -13,6 +13,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -78,7 +79,7 @@ public void calculateResources(JsonNode jsonNode, PlanConfigurationRequest planC
resultMap.put(output, result);
((ObjectNode) feature.get("properties")).put(output, result);
}
- planUtil.create(planConfigurationRequest,feature,resultMap,mappedValues);
+ planUtil.create(planConfigurationRequest, feature, resultMap, mappedValues, new HashMap<>());
}
}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java
index 9bfbe7cb62c..aa394c5a338 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/CampaignIntegrationUtil.java
@@ -3,7 +3,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.StringUtils;
import org.egov.processor.config.Configuration;
import org.egov.processor.config.ServiceConstants;
import org.egov.processor.repository.ServiceRequestRepository;
@@ -14,10 +13,7 @@
import org.egov.tracer.model.CustomException;
import org.springframework.stereotype.Component;
-import java.io.IOException;
-import java.math.BigDecimal;
import java.util.*;
-import java.util.Map.Entry;
import static org.egov.processor.config.ServiceConstants.*;
@@ -28,15 +24,11 @@ public class CampaignIntegrationUtil {
private ServiceRequestRepository serviceRequestRepository;
private Configuration config;
private ObjectMapper mapper;
- private ParsingUtil parsingUtil;
-
- public CampaignIntegrationUtil(ServiceRequestRepository serviceRequestRepository, Configuration config,
- ObjectMapper mapper, FilestoreUtil filestoreUtil, ParsingUtil parsingUtil) {
+ public CampaignIntegrationUtil(ServiceRequestRepository serviceRequestRepository, Configuration config, ObjectMapper mapper) {
this.serviceRequestRepository = serviceRequestRepository;
this.config = config;
this.mapper = mapper;
- this.parsingUtil= parsingUtil;
}
/**
@@ -82,34 +74,6 @@ private MicroplanDetailsRequest buildMicroplanDetailsForUpdate(PlanConfiguration
}
- /**
- * Updates campaign details based on the provided plan configuration request and response data.
- * This method integrates the campaign details obtained from the response into the provided plan configuration request.
- * It also updates the campaign boundaries and resources accordingly.
- *
- * @param planConfigurationRequest The plan configuration request containing the execution plan details.
- * @param response The response object containing campaign details.
- * @param campaignBoundaryList The list of campaign boundaries.
- * @param campaignResourcesList The list of campaign resources.
- */
- public void updateCampaignDetails(PlanConfigurationRequest planConfigurationRequest,Object response,List campaignBoundaryList,List campaignResourcesList) {
- CampaignResponse campaignResponse = null;
- try {
- campaignResponse = mapper.convertValue(response, CampaignResponse.class);
- campaignResponse.getCampaign().get(0).setResources(campaignResourcesList);
- Boundary[] array = campaignBoundaryList.toArray(new Boundary[0]);
- campaignResponse.getCampaign().get(0).setBoundaries(campaignBoundaryList.toArray(new Boundary[0]));
- serviceRequestRepository.fetchResult(
- new StringBuilder(config.getProjectFactoryHostEndPoint() + config.getCampaignIntegrationUpdateEndPoint()),
- buildCampaignRequestForUpdate(planConfigurationRequest, campaignResponse));
- log.info("Campaign Integration successful.");
- } catch (Exception e) {
- log.error(ServiceConstants.ERROR_WHILE_SEARCHING_CAMPAIGN
- + planConfigurationRequest.getPlanConfiguration().getCampaignId(), e);
- throw new CustomException("Failed to update campaign details in CampaignIntegration class within method updateCampaignDetails.", e.toString());
- }
- }
-
/**
* Sends a data creation request to the Project Factory service using the provided
* plan and campaign details.
@@ -131,41 +95,6 @@ public void createProjectFactoryDataCall(PlanConfigurationRequest planConfigurat
}
}
- /**
- * Updates the campaign resources in the given campaign response based on the files specified in the plan configuration request.
- *
- * @param campaignResponse The campaign response object to be updated with resources.
- * @param planConfigurationRequest The plan configuration request containing file information.
- * @param fileStoreId The file store ID.
- */
- public void updateResources(CampaignResponse campaignResponse, PlanConfigurationRequest planConfigurationRequest,
- String fileStoreId) {
- List campaignResourcesList = new ArrayList<>();
- List files = planConfigurationRequest.getPlanConfiguration().getFiles();
- for (File file : files) {
- CampaignResources campaignResource = new CampaignResources();
- campaignResource.setFilename(ServiceConstants.FILE_NAME);
- campaignResource.setFilestoreId(fileStoreId);
- campaignResource.setType(ServiceConstants.FILE_TYPE);
- campaignResourcesList.add(campaignResource);
- }
- campaignResponse.getCampaign().get(0).setResources(campaignResourcesList);
- }
-
- /**
- * Builds a campaign request object for updating campaign details based on the provided plan configuration request and campaign response.
- *
- * @param planConfigurationRequest The plan configuration request containing necessary information for updating the campaign.
- * @param campaignResponse The campaign response containing the updated campaign details.
- * @return The campaign request object built for updating campaign details.
- */
- private CampaignRequest buildCampaignRequestForUpdate(PlanConfigurationRequest planConfigurationRequest,
- CampaignResponse campaignResponse) {
- return CampaignRequest.builder().requestInfo(planConfigurationRequest.getRequestInfo())
- .campaignDetails(campaignResponse.getCampaign().get(0)).build();
-
- }
-
/**
* Builds a {@link ResourceDetailsRequest} object for facility creation using the provided
* plan configuration and campaign details.
@@ -202,121 +131,6 @@ private ResourceDetailsRequest buildResourceDetailsObjectForFacilityCreate(PlanC
}
- /**
- * Updates campaign boundary based on the provided plan configuration, feature, assumption values, mapped values, column index map, boundary list, and result map.
- *
- * @param planConfig The plan configuration containing relevant details.
- * @param feature The JSON node representing the feature.
- * @param assumptionValueMap The map containing assumption values.
- * @param mappedValues The map containing mapped values.
- * @param mapOfColumnNameAndIndex The map containing column names and their indices.
- * @param boundaryList The list of campaign boundaries to update.
- * @param resultMap The map containing result values.
- * @throws IOException If an I/O error occurs.
- */
- public void updateCampaignBoundary(PlanConfiguration planConfig, JsonNode feature,
- Map assumptionValueMap, Map mappedValues,
- Map mapOfColumnNameAndIndex, List boundaryList,
- Map resultMap) {
- Integer indexOfType = null;
- boolean validToAdd = false;
- Integer indexValue = 0;
- Boundary boundary = new Boundary();
- List> sortedColumnList = parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex);
- indexValue = parsingUtil.getIndexOfBoundaryCode(indexValue, sortedColumnList, mappedValues);
- prepareBoundary(indexOfType, indexValue, sortedColumnList, feature, boundary, mappedValues);
- if (isValidToAdd(boundaryList, resultMap, validToAdd, boundary))
- boundaryList.add(boundary);
- }
-
-
- /**
- * Prepares a campaign boundary based on the provided index values, sorted column list, feature, and mapped values.
- *
- * @param indexOfType The index of the boundary type.
- * @param indexValue The index value.
- * @param sortedColumnList The sorted list of column names and indices.
- * @param feature The JSON node representing the feature.
- * @param boundary The boundary object to be prepared.
- * @param mappedValues The map containing mapped values.
- * @return The index of the boundary type after preparation.
- */
- private Integer prepareBoundary(Integer indexOfType, Integer indexValue,
- List> sortedColumnList, JsonNode feature, Boundary boundary,Map mappedValues) {
- String codeValue = getBoundaryCodeValue(ServiceConstants.BOUNDARY_CODE, feature, mappedValues);
- boundary.setCode(codeValue);
- for (int j = 0; j < indexValue; j++) {
- Map.Entry entry = sortedColumnList.get(j);
- String value = String.valueOf(feature.get(PROPERTIES).get(entry.getKey()));
- if (StringUtils.isNotBlank(value) && value.length() > 2) {
- boundary.setType(entry.getKey());
- indexOfType = entry.getValue();
- }
- }
- if (indexOfType == 0) {
- boundary.setRoot(true);
- boundary.setIncludeAllChildren(true);
- }
- return indexOfType;
- }
-
- /**
- * Checks if the provided boundary is valid to add to the boundary list based on the result map.
- *
- * @param boundaryList The list of existing boundaries.
- * @param resultMap The map containing result values.
- * @param validToAdd The flag indicating whether the boundary is valid to add.
- * @param boundary The boundary to be checked for validity.
- * @return True if the boundary is valid to add, false otherwise.
- */
- private boolean isValidToAdd(List boundaryList, Map resultMap, boolean validToAdd,
- Boundary boundary) {
- for (Entry entry : resultMap.entrySet()) {
- if (entry.getValue().compareTo(new BigDecimal(0)) > 0) {
- validToAdd = true;
- } else {
- validToAdd = false;
- break;
- }
- }
- return validToAdd;
- }
-
-
-
- /**
- * Retrieves the value of the boundary code from the feature JSON node based on the mapped values.
- *
- * @param input The input key.
- * @param feature The JSON node representing the feature.
- * @param mappedValues The map containing mapped values.
- * @return The value of the boundary code.
- * @throws CustomException If the input value is not found in the feature JSON node.
- */
- private String getBoundaryCodeValue(String input, JsonNode feature, Map mappedValues) {
- if (feature.get(PROPERTIES).get(mappedValues.get(input)) != null) {
- String value = String.valueOf(feature.get(PROPERTIES).get(mappedValues.get(input)));
- return ((value != null && value.length() > 2) ? value.substring(1, value.length() - 1) : value);
- } else {
- throw new CustomException("INPUT_VALUE_NOT_FOUND", "Input value not found: " + input);
- }
- }
-
- /**
- * Updates campaign resources with the provided file store ID.
- *
- * @param fileStoreId The file store ID.
- * @param campaignResourcesList The list of campaign resources to update.
- */
- public void updateCampaignResources(String fileStoreId,List campaignResourcesList,String fileName) {
- CampaignResources campaignResource = new CampaignResources();
- campaignResource.setFilename(fileName);
- campaignResource.setFilestoreId(fileStoreId);
- campaignResource.setType(ServiceConstants.FILE_TYPE);
- campaignResourcesList.add(campaignResource);
-
- }
-
/**
* Builds a campaign search request based on the provided plan configuration request.
*
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/EnrichmentUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/EnrichmentUtil.java
index 89f0a89511b..20414382473 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/util/EnrichmentUtil.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/EnrichmentUtil.java
@@ -18,10 +18,12 @@
import org.egov.processor.web.models.mdmsV2.Mdms;
import org.egov.tracer.model.CustomException;
import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -43,12 +45,10 @@ public class EnrichmentUtil {
private PlanUtil planUtil;
private Configuration config;
-// private MultiStateInstanceUtil centralInstanceUtil;
public EnrichmentUtil(MdmsV2Util mdmsV2Util, LocaleUtil localeUtil, ParsingUtil parsingUtil, CensusUtil censusUtil, PlanUtil planUtil, Configuration config) {
this.mdmsV2Util = mdmsV2Util;
this.localeUtil = localeUtil;
-// this.centralInstanceUtil = centralInstanceUtil;
this.parsingUtil = parsingUtil;
this.censusUtil = censusUtil;
this.planUtil = planUtil;
@@ -65,7 +65,6 @@ public EnrichmentUtil(MdmsV2Util mdmsV2Util, LocaleUtil localeUtil, ParsingUtil
*/
public void enrichResourceMapping(PlanConfigurationRequest request, LocaleResponse localeResponse, String campaignType, String fileStoreId)
{
-// String rootTenantId = centralInstanceUtil.getStateLevelTenant(request.getPlanConfiguration().getTenantId());
String rootTenantId = request.getPlanConfiguration().getTenantId().split("\\.")[0];
String uniqueIndentifier = BOUNDARY + DOT_SEPARATOR + MICROPLAN_PREFIX + campaignType;
List mdmsV2Data = mdmsV2Util.fetchMdmsV2Data(request.getRequestInfo(), rootTenantId, MDMS_ADMIN_CONSOLE_MODULE_NAME + DOT_SEPARATOR + MDMS_SCHEMA_ADMIN_SCHEMA, uniqueIndentifier);
@@ -89,7 +88,7 @@ public void enrichResourceMapping(PlanConfigurationRequest request, LocaleRespon
}
- public void enrichsheetWithApprovedCensusRecords(Sheet sheet, PlanConfigurationRequest planConfigurationRequest, String fileStoreId, Map mappedValues) {
+ public void enrichsheetWithApprovedCensusRecords(Sheet sheet, PlanConfigurationRequest planConfigurationRequest, String fileStoreId, Map mappedValues, Map boundaryCodeToCensusAdditionalDetails) {
List boundaryCodes = getBoundaryCodesFromTheSheet(sheet, planConfigurationRequest, fileStoreId);
Map mapOfColumnNameAndIndex = parsingUtil.getAttributeNameIndexFromExcel(sheet);
@@ -143,6 +142,8 @@ public void enrichsheetWithApprovedCensusRecords(Sheet sheet, PlanConfigurationR
}
}
+
+ boundaryCodeToCensusAdditionalDetails.put(boundaryCode, census.getAdditionalDetails());
}
log.info("Successfully update file with approved census data.");
@@ -220,10 +221,10 @@ public void enrichsheetWithApprovedPlanEstimates(Sheet sheet, PlanConfigurationR
Integer indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0,
parsingUtil.sortColumnByIndex(mapOfColumnNameAndIndex), mappedValues);
- //Getting census records for the list of boundaryCodes
+ //Getting plan records for the list of boundaryCodes
List planList = getPlanRecordsForEnrichment(planConfigurationRequest, boundaryCodes);
- // Create a map from boundaryCode to Census for quick lookups
+ // Create a map from boundaryCode to Plan for quick lookups
Map planMap = planList.stream()
.collect(Collectors.toMap(Plan::getLocality, plan -> plan));
@@ -244,8 +245,13 @@ public void enrichsheetWithApprovedPlanEstimates(Sheet sheet, PlanConfigurationR
Plan planEstimate = planMap.get(boundaryCode);
if (planEstimate != null) {
- Map resourceTypeToEstimatedNumberMap = planEstimate.getResources().stream()
- .collect(Collectors.toMap(Resource::getResourceType, Resource::getEstimatedNumber));
+ Map resourceTypeToEstimatedNumberMap = new HashMap<>();
+
+ // If resources are not empty, iterate over each resource and map resourceType with it's estimatedValue.
+ if(!CollectionUtils.isEmpty(planEstimate.getResources()))
+ planEstimate.getResources().forEach(resource ->
+ resourceTypeToEstimatedNumberMap.put(resource.getResourceType(), resource.getEstimatedNumber()));
+
// Iterate over each output column to update the row cells with resource values
for (String resourceType : outputColumnList) {
@@ -262,6 +268,17 @@ public void enrichsheetWithApprovedPlanEstimates(Sheet sheet, PlanConfigurationR
}
cell.setCellValue(estimatedValue.doubleValue());
}
+ } else {
+ // If estimatedValue is null, set the cell to empty
+ Integer columnIndex = mapOfColumnNameAndIndex.get(resourceType);
+ if (columnIndex != null) {
+ // Ensure the cell is empty
+ Cell cell = row.getCell(columnIndex);
+ if (cell == null) {
+ cell = row.createCell(columnIndex);
+ }
+ cell.setCellValue(NOT_APPLICABLE); // Set as not applicable
+ }
}
}
}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/ExcelStylingUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/ExcelStylingUtil.java
new file mode 100644
index 00000000000..bdd1b7bea09
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/ExcelStylingUtil.java
@@ -0,0 +1,113 @@
+package org.egov.processor.util;
+
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xssf.usermodel.*;
+import org.springframework.stereotype.Component;
+
+import java.awt.Color;
+
+import static org.egov.processor.config.ServiceConstants.*;
+
+@Component
+public class ExcelStylingUtil {
+
+ public void styleCell(Cell cell) {
+ if(cell == null)
+ return;
+
+ Sheet sheet = cell.getSheet();
+ Workbook workbook = sheet.getWorkbook();
+ XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook;
+
+ // Create a cell style
+ XSSFCellStyle cellStyle;
+ try {
+ cellStyle = (XSSFCellStyle) workbook.createCellStyle();
+ } catch (Exception e) {
+ throw new IllegalStateException(ERROR_WHILE_CREATING_CELL_STYLE, e);
+ }
+
+ // Set background color
+ XSSFColor backgroundColor = hexToXSSFColor(HEX_BACKGROUND_COLOR, xssfWorkbook);
+ cellStyle.setFillForegroundColor(backgroundColor);
+ cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+
+ // Set font style (bold)
+ XSSFFont font = (XSSFFont) workbook.createFont();
+ font.setBold(true);
+ cellStyle.setFont(font);
+
+ // Set alignment and wrap text
+ cellStyle.setAlignment(HorizontalAlignment.LEFT);
+ cellStyle.setVerticalAlignment(VerticalAlignment.TOP);
+ cellStyle.setWrapText(true);
+
+ // Lock the cell if FREEZE_CELL is true
+ if (FREEZE_CELL) {
+ cellStyle.setLocked(true);
+ }
+
+ // Apply the style to the cell
+ cell.setCellStyle(cellStyle);
+
+ }
+
+ /**
+ * Adjusts the column width to fit the content of the given cell, adding padding for readability.
+ *
+ * @param cell the cell whose column width is to be adjusted; does nothing if null.
+ */
+ public void adjustColumnWidthForCell(Cell cell) {
+ if (cell == null) {
+ return;
+ }
+
+ Sheet sheet = cell.getSheet();
+ int columnIndex = cell.getColumnIndex();
+ int maxWidth = sheet.getColumnWidth(columnIndex);
+
+ // Calculate the width needed for the current cell content
+ String cellValue = cell.toString(); // Convert cell content to string
+ int cellWidth = cellValue.length() * 256; // Approximate width (1/256th of character width)
+
+ // Use the maximum width seen so far, including padding for readability
+ int padding = COLUMN_PADDING; // Adjust padding as needed
+ int newWidth = Math.max(maxWidth, cellWidth + padding);
+
+ sheet.setColumnWidth(columnIndex, newWidth);
+ }
+
+ /**
+ * Converts a HEX color string to XSSFColor using the XSSFWorkbook context.
+ *
+ * @param hexColor The HEX color string (e.g., "93C47D").
+ * @param xssfWorkbook The XSSFWorkbook context for styles.
+ * @return XSSFColor The corresponding XSSFColor object.
+ * @throws IllegalArgumentException if the hex color is null, empty, or in invalid format
+ */
+ public static XSSFColor hexToXSSFColor(String hexColor, XSSFWorkbook xssfWorkbook) {
+
+ if (hexColor == null || hexColor.length() < 6)
+ throw new IllegalArgumentException(INVALID_HEX + hexColor);
+
+ // Convert HEX to RGB
+ int red = Integer.valueOf(hexColor.substring(0, 2), 16);
+ int green = Integer.valueOf(hexColor.substring(2, 4), 16);
+ int blue = Integer.valueOf(hexColor.substring(4, 6), 16);
+
+ red = (int) (red * BRIGHTEN_FACTOR); // increase red component by 10%
+ green = (int) (green * BRIGHTEN_FACTOR); // increase green component by 10%
+ blue = (int) (blue * BRIGHTEN_FACTOR); // increase blue component by 10%
+
+ // Clamp the values to be between 0 and 255
+ red = Math.min(255, Math.max(0, red));
+ green = Math.min(255, Math.max(0, green));
+ blue = Math.min(255, Math.max(0, blue));
+
+ // Create IndexedColorMap from the workbook's styles source
+ IndexedColorMap colorMap = xssfWorkbook.getStylesSource().getIndexedColors();
+
+ // Create XSSFColor using the XSSFWorkbook context and colorMap
+ return new XSSFColor(new Color(red, green, blue), colorMap);
+ }
+}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsUtil.java
index e3019ded61b..5c168f8d30e 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsUtil.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsUtil.java
@@ -1,14 +1,6 @@
package org.egov.processor.util;
-import static org.egov.processor.config.ServiceConstants.ERROR_WHILE_FETCHING_FROM_MDMS;
-import static org.egov.processor.config.ServiceConstants.NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE;
-import static org.egov.processor.config.ServiceConstants.NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import org.egov.common.contract.request.RequestInfo;
import org.egov.mdms.model.MasterDetail;
@@ -22,6 +14,7 @@
import org.egov.tracer.model.CustomException;
import org.flywaydb.core.internal.util.JsonUtils;
import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
@@ -31,6 +24,8 @@
import lombok.extern.slf4j.Slf4j;
+import static org.egov.processor.config.ServiceConstants.*;
+
@Slf4j
@Component
public class MdmsUtil {
@@ -41,11 +36,14 @@ public class MdmsUtil {
private Configuration configs;
- public MdmsUtil(RestTemplate restTemplate, ObjectMapper mapper, Configuration configs) {
+ private ParsingUtil parsingUtil;
+
+ public MdmsUtil(RestTemplate restTemplate, ObjectMapper mapper, Configuration configs, ParsingUtil parsingUtil) {
this.restTemplate = restTemplate;
this.mapper = mapper;
this.configs = configs;
- }
+ this.parsingUtil = parsingUtil;
+ }
/**
* Fetches MDMS (Municipal Data Management System) data using the provided
@@ -74,7 +72,7 @@ public Object fetchMdmsData(RequestInfo requestInfo, String tenantId) {
if (result == null || ObjectUtils.isEmpty(result)) {
log.error(NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_MESSAGE + " - " + tenantId);
throw new CustomException(NO_MDMS_DATA_FOUND_FOR_GIVEN_TENANT_CODE,
- "no data found for the given tenantid "+tenantId + " for master name "+ServiceConstants.MDMS_MASTER_SCHEMAS);
+ "no data found for the given tenantid "+tenantId + " for master name "+ServiceConstants.MDMS_MASTER_ADMIN_SCHEMA);
}
return result;
}
@@ -87,10 +85,9 @@ public Object fetchMdmsData(RequestInfo requestInfo, String tenantId) {
* @return The MDMS criteria request object.
*/
public MdmsCriteriaReq getMdmsRequest(RequestInfo requestInfo, String tenantId) {
-
- ModuleDetail moduleDetail = getPlanModuleDetail();
+ ModuleDetail adminConsoleModuleDetail = getAdminConsoleModuleDetail();
List moduleDetails = new LinkedList<>();
- moduleDetails.add(moduleDetail);
+ moduleDetails.add(adminConsoleModuleDetail);
MdmsCriteria mdmsCriteria = MdmsCriteria.builder().moduleDetails(moduleDetails).tenantId(tenantId).build();
return MdmsCriteriaReq.builder().mdmsCriteria(mdmsCriteria).requestInfo(requestInfo).build();
}
@@ -109,34 +106,69 @@ private ModuleDetail getPlanModuleDetail() {
.moduleName(ServiceConstants.MDMS_PLAN_MODULE_NAME).build();
}
+ /**
+ * Retrieves the module details for the HCM-ADMIN-CONSOLE module.
+ *
+ * @return ModuleDetail object containing master details for the HCM-ADMIN-CONSOLE module.
+ */
+ private ModuleDetail getAdminConsoleModuleDetail() {
+ List adminSchemaMasterDetails = new ArrayList<>();
+ MasterDetail schemaDetails = MasterDetail.builder().name(ServiceConstants.MDMS_MASTER_ADMIN_SCHEMA).build();
+ adminSchemaMasterDetails.add(schemaDetails);
+
+ return ModuleDetail.builder().masterDetails(adminSchemaMasterDetails)
+ .moduleName(ServiceConstants.MDMS_ADMIN_CONSOLE_MODULE_NAME).build();
+ }
+
/**
* Filters master data based on the provided parameters.
*
* @param masterDataJson The JSON string representing the master data.
- * @param fileType The type of input file.
- * @param templateIdentifier The template identifier.
* @param campaignType The campaign type.
* @return A map containing filtered properties from the master data.
* @throws JsonMappingException if there's an issue mapping JSON.
* @throws JsonProcessingException if there's an issue processing JSON.
*/
- public Map filterMasterData(String masterDataJson, File.InputFileTypeEnum fileType,
- String templateIdentifier, String campaignType) {
+ public Map filterMasterData(String masterDataJson, String campaignType) {
Map properties = new HashMap<>();
- Map masterData = JsonUtils.parseJson(masterDataJson, Map.class);
- Map planModule = (Map) masterData.get(ServiceConstants.MDMS_PLAN_MODULE_NAME);
- List> schemas = (List>) planModule
- .get(ServiceConstants.MDMS_MASTER_SCHEMAS);
- log.info("masterDataJson ==>" + schemas);
- for (Map schema : schemas) {
- String type = (String) schema.get(ServiceConstants.MDMS_SCHEMA_TYPE);
- String campaign = (String) schema.get(ServiceConstants.MDMS_CAMPAIGN_TYPE);
- // String fileT = InputFileTypeEnum.valueOf(type);
- if (schema.get(ServiceConstants.MDMS_SCHEMA_SECTION).equals(ServiceConstants.FILE_TEMPLATE_IDENTIFIER_POPULATION)
- && campaign.equals(campaignType) && type.equals(fileType.toString())) {
- Map schemaProperties = (Map) schema.get("schema");
- properties = (Map) schemaProperties.get("Properties");
+ try {
+ Map masterData = JsonUtils.parseJson(masterDataJson, Map.class);
+ Map adminConsoleModule = (Map) masterData.get(ServiceConstants.MDMS_ADMIN_CONSOLE_MODULE_NAME);
+
+ //Extracting adminSchema master from the HCM-ADMIN-CONSOLE module
+ List> adminSchema = (List>) adminConsoleModule.get(ServiceConstants.MDMS_MASTER_ADMIN_SCHEMA);
+
+ log.debug("masterDataJson ==> " + adminSchema);
+
+ //Iterating through each schema in adminSchema master
+ for (Map schema : adminSchema) {
+ String campaign = (String) schema.get(ServiceConstants.MDMS_CAMPAIGN_TYPE);
+
+ // Skipping schema for which the required fields are missing to avoid null pointer exception
+ if (ObjectUtils.isEmpty(campaign) || ObjectUtils.isEmpty(schema.get(ServiceConstants.MDMS_SCHEMA_TITLE)))
+ continue;
+
+ // Check if the schema's title matches the required template identifier
+ // and the campaign matches the specified campaign type.
+ if (schema.get(ServiceConstants.MDMS_SCHEMA_TITLE).equals(ServiceConstants.FILE_TEMPLATE_IDENTIFIER_BOUNDARY)
+ && campaign.equals(MICROPLAN_PREFIX + campaignType)) {
+ Map> schemaProperties = (Map>) schema.get("properties");
+
+ // Skipping if schema properties are not present for the given campaign type.
+ if (CollectionUtils.isEmpty(schemaProperties))
+ continue;
+
+ schemaProperties.forEach((propertyType, propertyList) ->
+ // For each property in the property list, extract its name and add it to the map with the property.
+ propertyList.forEach(property -> {
+ String propertyName = (String) parsingUtil.extractFieldsFromJsonObject(property, "name");
+ properties.put(propertyName, property);
+ })
+ );
+ }
}
+ } catch (Exception e) {
+ log.error(ERROR_PROCESSING_DATA_FROM_MDMS, e);
}
return properties;
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsV2Util.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsV2Util.java
index 85b87872658..5430e0e55c6 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsV2Util.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/MdmsV2Util.java
@@ -67,9 +67,10 @@ private MdmsCriteriaReqV2 getMdmsV2Request(RequestInfo requestInfo, String tenan
MdmsCriteriaV2 mdmsCriteriaV2 = MdmsCriteriaV2.builder()
.tenantId(tenantId)
.schemaCode(schemaCode)
- .uniqueIdentifiers(Collections.singletonList(uniqueIdentifier))
- .limit(config.getDefaultLimit())
- .offset(config.getDefaultOffset()).build();
+ .uniqueIdentifiers(uniqueIdentifier != null ? Collections.singletonList(uniqueIdentifier) : null)
+ .limit(config.getDefaultLimitForMdms())
+ .offset(config.getDefaultOffsetForMdms())
+ .build();
return MdmsCriteriaReqV2.builder()
.requestInfo(requestInfo)
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/MixedStrategyUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/MixedStrategyUtil.java
new file mode 100644
index 00000000000..f82f258f438
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/MixedStrategyUtil.java
@@ -0,0 +1,119 @@
+package org.egov.processor.util;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.egov.processor.web.models.Operation;
+import org.egov.processor.web.models.PlanConfiguration;
+import org.egov.processor.web.models.PlanConfigurationRequest;
+import org.egov.processor.web.models.mdmsV2.Mdms;
+import org.egov.processor.web.models.mdmsV2.MixedStrategyOperationLogic;
+import org.egov.tracer.model.CustomException;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.egov.processor.config.ServiceConstants.*;
+
+@Component
+public class MixedStrategyUtil {
+
+ private MdmsV2Util mdmsV2Util;
+
+ private ParsingUtil parsingUtil;
+
+ private ObjectMapper mapper;
+
+ public MixedStrategyUtil(MdmsV2Util mdmsV2Util, ParsingUtil parsingUtil, ObjectMapper mapper) {
+ this.mdmsV2Util = mdmsV2Util;
+ this.parsingUtil = parsingUtil;
+ this.mapper = mapper;
+ }
+
+ /**
+ * Fetches a list of MixedStrategyOperationLogic objects from MDMS based on the provided planConfigurationRequest.
+ *
+ * @param request The PlanConfigurationRequest containing the plan configuration and request info.
+ * @return A list of MixedStrategyOperationLogic objects fetched from MDMS.
+ */
+ public List fetchMixedStrategyOperationLogicFromMDMS(PlanConfigurationRequest request) {
+ String rootTenantId = request.getPlanConfiguration().getTenantId().split("\\.")[0];
+ List mdmsV2Data = mdmsV2Util.fetchMdmsV2Data(request.getRequestInfo(), rootTenantId, MDMS_PLAN_MODULE_NAME + DOT_SEPARATOR + MDMS_MASTER_MIXED_STRATEGY, null);
+
+ if (CollectionUtils.isEmpty(mdmsV2Data)) {
+ throw new CustomException(NO_MDMS_DATA_FOUND_FOR_MIXED_STRATEGY_MASTER_CODE, NO_MDMS_DATA_FOUND_FOR_MIXED_STRATEGY_MASTER_CODE_MESSAGE);
+ }
+
+ return mdmsV2Data.stream()
+ .map(mdms -> mapper.convertValue(mdms.getData(), MixedStrategyOperationLogic.class))
+ .collect(Collectors.toList());
+
+ }
+
+ /**
+ * Retrieves a list of categories that are restricted based on the provided details.
+ * Returns an empty list if no match is found.
+ *
+ * @param isFixedPost A boolean indicating whether the mapped facility is fixed post.
+ * @param planConfiguration The plan configuration containing additional details.
+ * @param logicList A list of MixedStrategyOperationLogic objects to filter against.
+ * @return A list of categories not allowed to have output value or an empty list if no matching logic is found.
+ */
+ public List getCategoriesNotAllowed(boolean isFixedPost, PlanConfiguration planConfiguration, List logicList) {
+
+ //Extract fields from additional details
+ String registrationProcess = (String) parsingUtil.extractFieldsFromJsonObject(planConfiguration.getAdditionalDetails(), REGISTRATION_PROCESS);
+ String distributionProcess = (String) parsingUtil.extractFieldsFromJsonObject(planConfiguration.getAdditionalDetails(), DISTRIBUTION_PROCESS);
+
+ // If any of the process detail value is null, return an empty list
+ if (ObjectUtils.isEmpty(registrationProcess) || ObjectUtils.isEmpty(distributionProcess)) {
+ return Collections.emptyList();
+ }
+
+ return logicList.stream()
+ .filter(logic -> logic.isFixedPost() == isFixedPost)
+ .filter(logic -> logic.getRegistrationProcess().equalsIgnoreCase(registrationProcess))
+ .filter(logic -> logic.getDistributionProcess().equalsIgnoreCase(distributionProcess))
+ .map(MixedStrategyOperationLogic::getCategoriesNotAllowed)
+ .findAny() // Returns any matching element since there is only one match
+ .orElse(List.of());
+
+ }
+
+ /**
+ * Nullifies result values in the map for outputs belonging to the categories not allowed.
+ * Exits early if no restrictions are specified.
+ *
+ * @param resultMap A map containing output keys and their corresponding result values.
+ * @param operations A list of operations.
+ * @param categoriesNotAllowed A list of categories that are restricted and should not have associated outputs.
+ */
+ public void processResultMap(Map resultMap, List operations, List categoriesNotAllowed) {
+
+ // If all the categories are allowed, don't process further.
+ if(CollectionUtils.isEmpty(categoriesNotAllowed))
+ return;
+
+ // Map categories not allowed to its corresponding list of output keys
+ Map> categoryNotAllowedToOutputMap = operations.stream()
+ .filter(op -> op.getActive() && categoriesNotAllowed.contains(op.getCategory()))
+ .collect(Collectors.groupingBy(
+ Operation::getCategory,
+ Collectors.mapping(Operation::getOutput, Collectors.toList())));
+
+
+ // Iterate through categories in the categoriesNotAllowed list and set their result values to null
+ for (String category : categoriesNotAllowed) {
+ List outputKeys = categoryNotAllowedToOutputMap.getOrDefault(category, Collections.emptyList());
+ for (String outputKey : outputKeys) {
+ if (resultMap.containsKey(outputKey)) {
+ resultMap.put(outputKey, null);
+ }
+ }
+ }
+ }
+}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/OutputEstimationGenerationUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/OutputEstimationGenerationUtil.java
new file mode 100644
index 00000000000..b6417c45720
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/OutputEstimationGenerationUtil.java
@@ -0,0 +1,264 @@
+package org.egov.processor.util;
+
+import org.apache.poi.ss.usermodel.*;
+import org.egov.processor.web.models.Locale;
+import org.egov.processor.web.models.LocaleResponse;
+import org.egov.processor.web.models.PlanConfigurationRequest;
+import org.egov.processor.web.models.ResourceMapping;
+import org.egov.processor.web.models.census.Census;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.egov.tracer.model.CustomException;
+import org.springframework.util.ObjectUtils;
+
+import static org.egov.processor.config.ServiceConstants.*;
+
+@Component
+public class OutputEstimationGenerationUtil {
+
+ private LocaleUtil localeUtil;
+
+ private ParsingUtil parsingUtil;
+
+ private ExcelStylingUtil excelStylingUtil;
+
+ private EnrichmentUtil enrichmentUtil;
+
+ private MdmsUtil mdmsUtil;
+
+ public OutputEstimationGenerationUtil(LocaleUtil localeUtil, ParsingUtil parsingUtil, EnrichmentUtil enrichmentUtil, ExcelStylingUtil excelStylingUtil, MdmsUtil mdmsUtil) {
+ this.localeUtil = localeUtil;
+ this.parsingUtil = parsingUtil;
+ this.excelStylingUtil = excelStylingUtil;
+ this.enrichmentUtil = enrichmentUtil;
+ this.mdmsUtil = mdmsUtil;
+ }
+
+ /**
+ * Processes an output Excel workbook by removing unnecessary sheets, localizing header columns,
+ * and adding facility information for each boundary code. The configuration for processing
+ * is based on the provided PlanConfigurationRequest.
+ *
+ * @param workbook the Excel workbook to process
+ * @param request the PlanConfigurationRequest containing processing configuration
+ * @param filestoreId the identifier of the file store for additional processing requirements
+ */
+ public void processOutputFile(Workbook workbook, PlanConfigurationRequest request, String filestoreId) {
+ LocaleResponse localeResponse = localeUtil.searchLocale(request);
+ Map mdmsDataForCommonConstants = mdmsUtil.fetchMdmsDataForCommonConstants(
+ request.getRequestInfo(),
+ request.getPlanConfiguration().getTenantId());
+
+ // 1. removing readme sheet
+ for (int i = workbook.getNumberOfSheets() - 1; i >= 0; i--) {
+ Sheet sheet = workbook.getSheetAt(i);
+ if (!isSheetAllowedToProcess(sheet.getSheetName(), localeResponse, mdmsDataForCommonConstants)) {
+ workbook.removeSheetAt(i);
+ }
+ }
+
+ // 2. Stylize and localize output column headers
+ for(Sheet sheet: workbook) {
+ processSheetForHeaderLocalization(sheet, localeResponse);
+ }
+
+ // 3. Adding facility information for each boundary code
+ for(Sheet sheet: workbook) {
+ addAssignedFacility(sheet, request, filestoreId);
+ }
+
+ }
+
+ /**
+ * Localizes the header row in the given sheet using the provided localization map.
+ * Applies styling and adjusts column widths for each localized header cell.
+ *
+ * @param sheet the Excel sheet whose header row needs localization
+ * @param localeResponse localization search call response
+ */
+ public void processSheetForHeaderLocalization(Sheet sheet, LocaleResponse localeResponse) {
+ // create localization code and message map
+ Map localizationCodeAndMessageMap = localeResponse.getMessages().stream()
+ .collect(Collectors.toMap(
+ Locale::getCode,
+ Locale::getMessage,
+ (existingValue, newValue) -> existingValue // Keep the existing value in case of duplicates
+ ));
+
+ // Fetch the header row from sheet
+ Row row = sheet.getRow(0);
+ if (parsingUtil.isRowEmpty(row))
+ throw new CustomException(EMPTY_HEADER_ROW_CODE, EMPTY_HEADER_ROW_MESSAGE);
+
+
+ //Iterate from the end, for every cell localize the header value
+ for (int i = row.getLastCellNum() - 1; i >= 0; i--) {
+ Cell headerColumn = row.getCell(i);
+
+ if (headerColumn == null || headerColumn.getCellType() != CellType.STRING) {
+ continue;
+ }
+ String headerColumnValue = headerColumn.getStringCellValue();
+
+ // Exit the loop if the header column value is not in the localization map
+ if (!localizationCodeAndMessageMap.containsKey(headerColumnValue)) {
+ break;
+ }
+
+ // Update the cell value with the localized message
+ excelStylingUtil.styleCell(headerColumn);
+ headerColumn.setCellValue(localizationCodeAndMessageMap.get(headerColumnValue));
+ excelStylingUtil.adjustColumnWidthForCell(headerColumn);
+ }
+
+ }
+
+ /**
+ * This is the main method responsible for adding an assigned facility name column to each sheet in the workbook.
+ * It iterates through all the sheets, verifies if they are eligible for processing, retrieves required mappings
+ * and boundary codes, and populates the new column with facility names based on these mappings.
+ *
+ * @param sheet the sheet to be processed.
+ * @param request the plan configuration request containing the resource mapping and other configurations.
+ * @param fileStoreId the associated file store ID used to filter resource mappings.
+ */
+ public void addAssignedFacility(Sheet sheet, PlanConfigurationRequest request, String fileStoreId) {
+ LocaleResponse localeResponse = localeUtil.searchLocale(request);
+
+ // Get the localized column header name for assigned facilities.
+ String assignedFacilityColHeader = localeUtil.localeSearch(localeResponse.getMessages(), HCM_MICROPLAN_SERVING_FACILITY);
+
+ // Default to a constant value if no localized value is found.
+ assignedFacilityColHeader = assignedFacilityColHeader != null ? assignedFacilityColHeader : HCM_MICROPLAN_SERVING_FACILITY;
+
+ // Creating a map of MappedTo and MappedFrom values from resource mapping
+ Map mappedValues = request.getPlanConfiguration().getResourceMapping().stream()
+ .filter(f -> f.getFilestoreId().equals(fileStoreId))
+ .collect(Collectors.toMap(
+ ResourceMapping::getMappedTo,
+ ResourceMapping::getMappedFrom,
+ (existing, replacement) -> existing,
+ LinkedHashMap::new
+ ));
+
+ // Create the map of boundary code to the facility assigned to that boundary.
+ Map boundaryCodeToFacility = getBoundaryCodeToFacilityMap(sheet, request, fileStoreId);
+
+ // Add facility names to the sheet.
+ addFacilityNameToSheet(sheet, assignedFacilityColHeader, boundaryCodeToFacility, mappedValues);
+ }
+
+ /**
+ * Collects boundary codes from all eligible sheets in the workbook, fetches census records for these boundaries,
+ * and maps each boundary code to its assigned facility name obtained from the census data.
+ *
+ * @param sheet the sheet to be processed.
+ * @param request the plan configuration request with boundary code details.
+ * @param fileStoreId the associated file store ID for filtering.
+ * @return a map of boundary codes to their assigned facility names.
+ */
+ public Map getBoundaryCodeToFacilityMap(Sheet sheet, PlanConfigurationRequest request, String fileStoreId) {
+ List boundaryCodes = new ArrayList<>();
+
+ // Extract boundary codes from the sheet.
+ boundaryCodes.addAll(enrichmentUtil.getBoundaryCodesFromTheSheet(sheet, request, fileStoreId));
+
+ // Fetch census records for the extracted boundary codes.
+ List censusList = enrichmentUtil.getCensusRecordsForEnrichment(request, boundaryCodes);
+ return censusList.stream()
+ .collect(Collectors.toMap(
+ Census::getBoundaryCode,
+ census -> (String) parsingUtil.extractFieldsFromJsonObject(census.getAdditionalDetails(), FACILITY_NAME)));
+ }
+
+ /**
+ * Processes a given sheet by adding a new column for assigned facilities and populating
+ * each row with the corresponding facility name based on the boundary code.
+ *
+ * @param sheet the sheet being processed.
+ * @param assignedFacilityColHeader the header for the new assigned facility column.
+ * @param boundaryCodeToFacility the mapping of boundary codes to assigned facilities.
+ * @param mappedValues a map of 'MappedTo' to 'MappedFrom' values.
+ */
+ private void addFacilityNameToSheet(Sheet sheet, String assignedFacilityColHeader, Map boundaryCodeToFacility, Map mappedValues) {
+ int indexOfFacility = createAssignedFacilityColumn(sheet, assignedFacilityColHeader);
+
+ // Get column index mappings from the sheet.
+ Map columnNameIndexMap = parsingUtil.getAttributeNameIndexFromExcel(sheet);
+
+ // Get the index of the boundary code column based on the provided mappings.
+ int indexOfBoundaryCode = parsingUtil.getIndexOfBoundaryCode(0, parsingUtil.sortColumnByIndex(columnNameIndexMap), mappedValues);
+
+ // Iterate over each row in the sheet and set the facility name for each row.
+ for (Row row : sheet) {
+ if (row.getRowNum() == 0 || parsingUtil.isRowEmpty(row)) {
+ continue;
+ }
+
+ String boundaryCode = row.getCell(indexOfBoundaryCode).getStringCellValue();
+
+ // Get or create the facility cell in the row.
+ Cell facilityCell = row.getCell(indexOfFacility);
+ if (facilityCell == null) {
+ facilityCell = row.createCell(indexOfFacility, CellType.STRING);
+ }
+
+ // Assign the facility name based on the boundary code.
+ facilityCell.setCellValue(boundaryCodeToFacility.getOrDefault(boundaryCode, EMPTY_STRING));
+ facilityCell.getCellStyle().setLocked(false); // Ensure the new cell is editable
+
+ }
+ }
+
+ /**
+ * Adds a new column for the assigned facility name in the provided sheet, styles the header cell,
+ * and returns the index of the newly created column.
+ *
+ * @param sheet the sheet where the column is to be added.
+ * @param assignedFacilityColHeader the header for the new column.
+ * @return the index of the newly created column.
+ */
+ private int createAssignedFacilityColumn(Sheet sheet, String assignedFacilityColHeader) {
+ int indexOfFacility = (int) sheet.getRow(0).getLastCellNum();
+
+ // Create a new cell for the column header.
+ Cell facilityColHeader = sheet.getRow(0).createCell(indexOfFacility, CellType.STRING);
+
+ //stylize cell and set cell value as the localized value
+ excelStylingUtil.styleCell(facilityColHeader);
+ facilityColHeader.setCellValue(assignedFacilityColHeader);
+ excelStylingUtil.adjustColumnWidthForCell(facilityColHeader);
+ return indexOfFacility;
+ }
+
+ /**
+ * Checks if a sheet is allowed to be processed based on MDMS constants and locale-specific configuration.
+ *
+ * @param sheetName The name of the sheet to be processed.
+ * @param localeResponse Localisation response for the given tenant id.
+ * @param mdmsDataConstants Mdms data for common constants.
+ * @return true if the sheet is allowed to be processed, false otherwise.
+ */
+ public boolean isSheetAllowedToProcess(String sheetName, LocaleResponse localeResponse, Map mdmsDataConstants) {
+
+ String readMeSheetName = (String) mdmsDataConstants.get(READ_ME_SHEET_NAME);
+ if(ObjectUtils.isEmpty(readMeSheetName))
+ throw new CustomException(README_SHEET_NAME_LOCALISATION_NOT_FOUND_CODE, README_SHEET_NAME_LOCALISATION_NOT_FOUND_MESSAGE);
+
+ for (Locale locale : localeResponse.getMessages()) {
+ if ((locale.getCode().equalsIgnoreCase((String) mdmsDataConstants.get(READ_ME_SHEET_NAME)))
+ || locale.getCode().equalsIgnoreCase(HCM_ADMIN_CONSOLE_BOUNDARY_DATA)) {
+ if (sheetName.equals(locale.getMessage()))
+ return false;
+ }
+ }
+ return true;
+
+ }
+}
+
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/ParsingUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/ParsingUtil.java
index ba10327e4c8..b5f20eaf36f 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/util/ParsingUtil.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/ParsingUtil.java
@@ -3,12 +3,13 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.DecimalNode;
+import com.fasterxml.jackson.databind.node.NullNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.poi.ss.usermodel.*;
import org.egov.processor.config.ServiceConstants;
-import org.egov.processor.web.models.PlanConfiguration;
-import org.egov.processor.web.models.ResourceMapping;
+import org.egov.processor.web.models.*;
import org.egov.tracer.model.CustomException;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
@@ -17,6 +18,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
@@ -24,22 +26,19 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-import static org.egov.processor.config.ServiceConstants.PROPERTIES;
+import static org.egov.processor.config.ServiceConstants.*;
@Slf4j
@Component
public class ParsingUtil {
- private PlanConfigurationUtil planConfigurationUtil;
-
private FilestoreUtil filestoreUtil;
- private CalculationUtil calculationUtil;
+ private ObjectMapper objectMapper;
- public ParsingUtil(PlanConfigurationUtil planConfigurationUtil, FilestoreUtil filestoreUtil, CalculationUtil calculationUtil) {
- this.planConfigurationUtil = planConfigurationUtil;
+ public ParsingUtil(FilestoreUtil filestoreUtil, ObjectMapper objectMapper) {
this.filestoreUtil = filestoreUtil;
- this.calculationUtil = calculationUtil;
+ this.objectMapper = objectMapper;
}
public List fetchAttributeNamesFromJson(JsonNode jsonNode)
@@ -314,7 +313,7 @@ public List extractPropertyNamesFromAdminSchema(JsonNode rootNode) {
* @param row the Row to check
* @return true if the row is empty, false otherwise
*/
- public static boolean isRowEmpty(Row row) {
+ public boolean isRowEmpty(Row row) {
if (row == null) {
return true;
}
@@ -392,4 +391,65 @@ public void printRow(Sheet sheet, Row row) {
System.out.println(); // Move to the next line after printing the row
}
+ /**
+ * Extracts provided field from the additional details object
+ *
+ * @param additionalDetails the additionalDetails object from PlanConfigurationRequest
+ * @param fieldToExtract the name of the field to be extracted from the additional details
+ * @return the value of the specified field as a string
+ * @throws CustomException if the field does not exist
+ */
+ public Object extractFieldsFromJsonObject(Object additionalDetails, String fieldToExtract) {
+ try {
+ String jsonString = objectMapper.writeValueAsString(additionalDetails);
+ JsonNode rootNode = objectMapper.readTree(jsonString);
+
+ JsonNode node = rootNode.get(fieldToExtract);
+ if (node != null && !node.isNull()) {
+
+ // Check for different types of JSON nodes
+ if (node.isDouble() || node.isFloat()) {
+ return BigDecimal.valueOf(node.asDouble()); // Convert Double to BigDecimal
+ } else if (node.isLong() || node.isInt()) {
+ return BigDecimal.valueOf(node.asLong()); // Convert Long to BigDecimal
+ } else if (node.isBoolean()) {
+ return node.asBoolean();
+ } else if (node.isTextual()) {
+ return node.asText();
+ } else if (node.isObject()) {
+ return objectMapper.convertValue(node, Map.class); // Return the object node as a Map
+ }
+ }
+ log.debug("The field to be extracted - " + fieldToExtract + " is not present in additional details.");
+ return null;
+ } catch (Exception e) {
+ log.error(e.getMessage() + fieldToExtract);
+ throw new CustomException(PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_CODE, PROVIDED_KEY_IS_NOT_PRESENT_IN_JSON_OBJECT_MESSAGE + fieldToExtract);
+ }
+ }
+
+ /**
+ * Adds or updates the value of provided field in the additional details object.
+ * @param additionalDetails
+ * @param fieldsToBeUpdated
+ * @return
+ */
+ public Map updateFieldInAdditionalDetails(Object additionalDetails, Map fieldsToBeUpdated) {
+ try {
+
+ // Get or create the additionalDetails as an ObjectNode
+ ObjectNode objectNode = (additionalDetails == null || additionalDetails instanceof NullNode)
+ ? objectMapper.createObjectNode()
+ : objectMapper.convertValue(additionalDetails, ObjectNode.class);
+
+ // Update or add the field in additional details object
+ fieldsToBeUpdated.forEach((key, value) -> objectNode.set(key, objectMapper.valueToTree(value)));
+
+ // Convert updated ObjectNode back to a Map
+ return objectMapper.convertValue(objectNode, Map.class);
+
+ } catch (Exception e) {
+ throw new CustomException(ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_CODE, ERROR_WHILE_UPDATING_ADDITIONAL_DETAILS_MESSAGE + e);
+ }
+ }
}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanFacilityUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanFacilityUtil.java
new file mode 100644
index 00000000000..84673539339
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanFacilityUtil.java
@@ -0,0 +1,53 @@
+package org.egov.processor.util;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.egov.processor.config.Configuration;
+import org.egov.processor.config.ServiceConstants;
+import org.egov.processor.repository.ServiceRequestRepository;
+import org.springframework.stereotype.Component;
+import org.egov.processor.web.models.planFacility.*;
+
+@Slf4j
+@Component
+public class PlanFacilityUtil {
+ private Configuration config;
+
+ private ServiceRequestRepository serviceRequestRepository;
+
+ private ObjectMapper mapper;
+
+ public PlanFacilityUtil(Configuration config, ServiceRequestRepository serviceRequestRepository, ObjectMapper mapper) {
+ this.config = config;
+ this.serviceRequestRepository = serviceRequestRepository;
+ this.mapper = mapper;
+ }
+
+ /**
+ * Searches for plan facilities based on the provided search request.
+ *
+ * @param planfacilitySearchRequest The search request containing the search criteria.
+ * @return A response with a list of plan facilities that matches the search criteria.
+ */
+ public PlanFacilityResponse search(PlanFacilitySearchRequest planfacilitySearchRequest) {
+
+ PlanFacilityResponse planFacilityResponse = null;
+ try {
+ Object response = serviceRequestRepository.fetchResult(getPlanFacilitySearchUri(), planfacilitySearchRequest);
+ planFacilityResponse = mapper.convertValue(response, PlanFacilityResponse.class);
+
+ } catch (Exception e) {
+ log.error(ServiceConstants.ERROR_WHILE_SEARCHING_PLAN_FACILITY, e);
+ }
+
+ return planFacilityResponse;
+ }
+
+ /**
+ * Creates a search uri for plan facility search.
+ * @return
+ */
+ private StringBuilder getPlanFacilitySearchUri() {
+ return new StringBuilder().append(config.getPlanConfigHost()).append(config.getPlanFacilitySearchEndPoint());
+ }
+}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanUtil.java b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanUtil.java
index 97c9810e0c5..1a6ee1e7cda 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanUtil.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/util/PlanUtil.java
@@ -14,10 +14,10 @@
import org.egov.tracer.model.CustomException;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Map;
+import java.util.*;
import java.util.stream.Collectors;
import static org.egov.processor.config.ServiceConstants.*;
@@ -33,11 +33,14 @@ public class PlanUtil {
private ObjectMapper mapper;
- public PlanUtil(ServiceRequestRepository serviceRequestRepository, Configuration config, Producer producer, ObjectMapper mapper) {
+ private ParsingUtil parsingUtil;
+
+ public PlanUtil(ServiceRequestRepository serviceRequestRepository, Configuration config, Producer producer, ObjectMapper mapper, ParsingUtil parsingUtil) {
this.serviceRequestRepository = serviceRequestRepository;
this.config = config;
this.producer = producer;
this.mapper = mapper;
+ this.parsingUtil = parsingUtil;
}
/**
@@ -47,10 +50,11 @@ public PlanUtil(ServiceRequestRepository serviceRequestRepository, Configuration
* @param feature The feature JSON node.
* @param resultMap The result map.
* @param mappedValues The mapped values.
+ * @param boundaryCodeToCensusAdditionalDetails A Map of boundary code to censusAdditionalDetails for that boundary code.
*/
public void create(PlanConfigurationRequest planConfigurationRequest, JsonNode feature,
- Map resultMap, Map mappedValues) {
- PlanRequest planRequest = buildPlanRequest(planConfigurationRequest, feature, resultMap, mappedValues);
+ Map resultMap, Map mappedValues, Map boundaryCodeToCensusAdditionalDetails) {
+ PlanRequest planRequest = buildPlanRequest(planConfigurationRequest, feature, resultMap, mappedValues, boundaryCodeToCensusAdditionalDetails);
try {
producer.push(config.getResourceMicroplanCreateTopic(), planRequest);
} catch (Exception e) {
@@ -61,25 +65,27 @@ public void create(PlanConfigurationRequest planConfigurationRequest, JsonNode f
/**
* Builds a PlanRequest object using the provided plan configuration request, feature JSON node,
* result map, mapped values, and assumption value map.
- *
+ *
* @param planConfigurationRequest The plan configuration request.
* @param feature The feature JSON node.
* @param resultMap The result map.
* @param mappedValues The mapped values.
+ * @param boundaryCodeToCensusAdditionalDetails A Map of boundary code to censusAdditionalDetails for that boundary code.
* @return The constructed PlanRequest object.
*/
private PlanRequest buildPlanRequest(PlanConfigurationRequest planConfigurationRequest, JsonNode feature,
- Map resultMap, Map mappedValues) {
+ Map resultMap, Map mappedValues, Map boundaryCodeToCensusAdditionalDetails) {
PlanConfiguration planConfig = planConfigurationRequest.getPlanConfiguration();
+ String boundaryCodeValue = getBoundaryCodeValue(ServiceConstants.BOUNDARY_CODE, feature, mappedValues);
+
return PlanRequest.builder()
.requestInfo(planConfigurationRequest.getRequestInfo())
.plan(Plan.builder()
.tenantId(planConfig.getTenantId())
.planConfigurationId(planConfig.getId())
.campaignId(planConfig.getCampaignId())
- .locality(getBoundaryCodeValue(ServiceConstants.BOUNDARY_CODE,
- feature, mappedValues))
+ .locality(boundaryCodeValue)
.resources(resultMap.entrySet().stream().map(result -> {
Resource res = new Resource();
res.setResourceType(result.getKey());
@@ -90,11 +96,77 @@ private PlanRequest buildPlanRequest(PlanConfigurationRequest planConfigurationR
.targets(new ArrayList())
.workflow(Workflow.builder().action(WORKFLOW_ACTION_INITIATE).build())
.isRequestFromResourceEstimationConsumer(true)
+ .additionalDetails(enrichAdditionalDetails(boundaryCodeToCensusAdditionalDetails, boundaryCodeValue))
.build())
.build();
+ }
+
+ /**
+ * Creates an additional details object. Extracts the required fields from census additional details for the given boundary.
+ * The extracted fields are then used to update the additional details object.
+ *
+ * @param boundaryCodeToCensusAdditionalDetails A map containing boundary codes mapped to their respective census additional details.
+ * @param boundaryCodeValue The boundary code for which additional details need to be enriched.
+ * @return An updated object containing extracted and enriched additional details, or null if no details were found or added.
+ */
+ private Object enrichAdditionalDetails(Map boundaryCodeToCensusAdditionalDetails, String boundaryCodeValue) {
+ if(!CollectionUtils.isEmpty(boundaryCodeToCensusAdditionalDetails)) {
+
+ Object censusAdditionalDetails = boundaryCodeToCensusAdditionalDetails.get(boundaryCodeValue);
+
+ // Return null value if censusAdditionalDetails is null
+ if(ObjectUtils.isEmpty(censusAdditionalDetails))
+ return null;
+
+ // Extract required details from census additional details object.
+ String facilityId = (String) parsingUtil.extractFieldsFromJsonObject(censusAdditionalDetails, FACILITY_ID);
+ Object accessibilityDetails = (Object) parsingUtil.extractFieldsFromJsonObject(censusAdditionalDetails, ACCESSIBILITY_DETAILS);
+ Object securityDetails = (Object) parsingUtil.extractFieldsFromJsonObject(censusAdditionalDetails, SECURITY_DETAILS);
+
+ // Creating a map of fields to be added in plan additional details with their key.
+ Map fieldsToBeUpdated = new HashMap<>();
+ if(!ObjectUtils.isEmpty(facilityId))
+ fieldsToBeUpdated.put(FACILITY_ID, facilityId);
+
+ // Add fields from accessibilityDetails to fieldsToBeUpdated map if it's present in censusAdditionalDetails.
+ if(!ObjectUtils.isEmpty(accessibilityDetails)) {
+ extractNestedFields((Map) accessibilityDetails, ACCESSIBILITY_DETAILS, fieldsToBeUpdated);
+ }
+
+ // Add fields from securityDetails to fieldsToBeUpdated map if it's present in censusAdditionalDetails.
+ if(!ObjectUtils.isEmpty(securityDetails)) {
+ extractNestedFields((Map) securityDetails, SECURITY_DETAILS, fieldsToBeUpdated);
+ }
+
+ // If the fieldsToBeUpdated map is not empty, pass a new empty object to serve as the additional details object.
+ if(!CollectionUtils.isEmpty(fieldsToBeUpdated))
+ return parsingUtil.updateFieldInAdditionalDetails(new Object(), fieldsToBeUpdated);
+ }
+ return null;
+ }
+ /**
+ * Extracts nested fields from the given additionalDetails map and adds them to fieldsToBeUpdated in a structured format.
+ * If a nested map contains CODE, its value is stored with the key formatted as "prefix|key|CODE".
+ *
+ * @param details The map containing nested key-value pairs to be processed.
+ * @param prefix The prefix to be used for constructing the final key in fieldsToBeUpdated.
+ * @param fieldsToBeUpdated The map where extracted values will be stored with formatted keys.
+ */
+ private void extractNestedFields(Map details, String prefix, Map fieldsToBeUpdated) {
+ for (Map.Entry entry : details.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ if (value instanceof Map) {
+ Map nestedMap = (Map) value;
+ if (nestedMap.containsKey(CODE)) {
+ fieldsToBeUpdated.put(prefix + "|" + key + "|" + CODE, nestedMap.get(CODE));
+ }
+ }
+ }
}
-
+
/**
* Retrieves the boundary code value from the feature JSON node using the mapped value for the given input.
*
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Resource.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Resource.java
index 693ba12ef32..c33fd233ec9 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Resource.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/Resource.java
@@ -29,7 +29,6 @@ public class Resource {
private String resourceType = null;
@JsonProperty("estimatedNumber")
- @NotNull
private BigDecimal estimatedNumber = null;
@JsonProperty("activityCode")
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/Census.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/Census.java
index f895f6c75f1..8db4e8b6ec6 100644
--- a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/Census.java
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/census/Census.java
@@ -48,8 +48,7 @@ public class Census {
private String boundaryCode = null;
@JsonProperty("assignee")
- @Size(max = 64)
- private String assignee = null;
+ private List assignee = null;
@JsonProperty("status")
@Size(max = 64)
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MixedStrategyOperationLogic.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MixedStrategyOperationLogic.java
new file mode 100644
index 00000000000..19f73f32e4e
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/mdmsV2/MixedStrategyOperationLogic.java
@@ -0,0 +1,34 @@
+package org.egov.processor.web.models.mdmsV2;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+/**
+ * MixedStrategyOperationLogic:
+ * Represents the configuration logic for mixed strategy operations in microplanning
+ */
+@Validated
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class MixedStrategyOperationLogic {
+
+ @JsonProperty("isFixedPost")
+ private boolean isFixedPost;
+
+ @JsonProperty("RegistrationProcess")
+ private String registrationProcess;
+
+ @JsonProperty("DistributionProcess")
+ private String distributionProcess;
+
+ @JsonProperty("CategoriesNotAllowed")
+ private List categoriesNotAllowed;
+}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacility.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacility.java
new file mode 100644
index 00000000000..7111050551b
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacility.java
@@ -0,0 +1,89 @@
+package org.egov.processor.web.models.planFacility;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.egov.common.contract.models.AuditDetails;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Plan Facility
+ */
+@Validated
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class PlanFacility {
+
+ @JsonProperty("id")
+ private String id = null;
+
+ @JsonProperty("tenantId")
+ @NotNull
+ @Size(min = 2, max = 64)
+ private String tenantId = null;
+
+ @JsonProperty("planConfigurationId")
+ @NotNull
+ @Size(max = 64)
+ private String planConfigurationId = null;
+
+ @JsonProperty("planConfigurationName")
+ private String planConfigurationName = null;
+
+ @JsonProperty("boundaryAncestralPath")
+ private String boundaryAncestralPath = null;
+
+ @JsonProperty("facilityId")
+ @NotNull
+ @Size(max = 64)
+ private String facilityId = null;
+
+ @JsonProperty("facilityName")
+ private String facilityName = null;
+
+ @JsonProperty("residingBoundary")
+ @NotNull
+ @Size(min = 1, max = 64)
+ private String residingBoundary = null;
+
+ @JsonProperty("serviceBoundaries")
+ @NotNull
+ @Valid
+ private List serviceBoundaries;
+
+ @JsonIgnore
+ private List initiallySetServiceBoundaries;
+
+ @JsonProperty("jurisdictionMapping")
+ private Map jurisdictionMapping;
+
+ @JsonProperty("additionalDetails")
+ private Object additionalDetails = null;
+
+ @JsonProperty("active")
+ @NotNull
+ private Boolean active = Boolean.TRUE;
+
+ @JsonProperty("auditDetails")
+ private AuditDetails auditDetails = null;
+
+ public PlanFacility addServiceBoundariesItem(String serviceBoundariesItem) {
+ if (this.serviceBoundaries == null) {
+ this.serviceBoundaries = new ArrayList<>();
+ }
+ this.serviceBoundaries.add(serviceBoundariesItem);
+ return this;
+ }
+}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilityResponse.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilityResponse.java
new file mode 100644
index 00000000000..4737171d908
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilityResponse.java
@@ -0,0 +1,33 @@
+package org.egov.processor.web.models.planFacility;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.egov.common.contract.response.ResponseInfo;
+import org.springframework.validation.annotation.Validated;
+import java.util.List;
+
+/**
+ * PlanFacilityResponse
+ */
+@Validated
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class PlanFacilityResponse {
+
+ @JsonProperty("ResponseInfo")
+ private ResponseInfo responseInfo;
+
+ @JsonProperty("PlanFacility")
+ @Valid
+ private List planFacility;
+
+ @JsonProperty("TotalCount")
+ @Valid
+ private Integer totalCount;
+}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilitySearchCriteria.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilitySearchCriteria.java
new file mode 100644
index 00000000000..78b308dc12a
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilitySearchCriteria.java
@@ -0,0 +1,71 @@
+package org.egov.processor.web.models.planFacility;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * PlanFacilitySearchCriteria
+ */
+@Validated
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class PlanFacilitySearchCriteria {
+
+ @JsonProperty("ids")
+ private Set ids = null;
+
+ @JsonProperty("tenantId")
+ @NotNull
+ private String tenantId = null;
+
+ @JsonProperty("planConfigurationId")
+ @NotNull
+ private String planConfigurationId = null;
+
+ @JsonProperty("planConfigurationName")
+ private String planConfigurationName = null;
+
+ @JsonProperty("facilityName")
+ private String facilityName = null;
+
+ @JsonProperty("facilityStatus")
+ private String facilityStatus = null;
+
+ @JsonProperty("facilityType")
+ private String facilityType = null;
+
+ @JsonProperty("residingBoundaries")
+ private List residingBoundaries = null;
+
+ @JsonProperty("jurisdiction")
+ private List jurisdiction = null;
+
+ @JsonProperty("facilityId")
+ private String facilityId = null;
+
+ @JsonProperty("offset")
+ @Min(0)
+ private Integer offset = null;
+
+ @JsonProperty("limit")
+ @Min(1)
+ private Integer limit = null;
+
+ @JsonIgnore
+ private Map filtersMap = null;
+
+}
diff --git a/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilitySearchRequest.java b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilitySearchRequest.java
new file mode 100644
index 00000000000..91fd4fa17c6
--- /dev/null
+++ b/health-services/resource-generator/src/main/java/org/egov/processor/web/models/planFacility/PlanFacilitySearchRequest.java
@@ -0,0 +1,34 @@
+package org.egov.processor.web.models.planFacility;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.Valid;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.egov.common.contract.request.RequestInfo;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * PlanFacilitySearchRequest
+ */
+@Validated
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+public class PlanFacilitySearchRequest {
+
+ @JsonProperty("RequestInfo")
+ @NotNull
+ @Valid
+ private RequestInfo requestInfo = null;
+
+ @JsonProperty("PlanFacilitySearchCriteria")
+ @NotNull
+ @Valid
+ private PlanFacilitySearchCriteria planFacilitySearchCriteria = null;
+
+}
diff --git a/health-services/resource-generator/src/main/resources/application.properties b/health-services/resource-generator/src/main/resources/application.properties
index 45e36a74e5c..f5f8ba1c4f1 100644
--- a/health-services/resource-generator/src/main/resources/application.properties
+++ b/health-services/resource-generator/src/main/resources/application.properties
@@ -49,11 +49,6 @@ egov.mdms.host=https://unified-dev.digit.org
egov.mdms.search.endpoint=/egov-mdms-service/v1/_search
egov.mdms.search.v2.endpoint=/mdms-v2/v2/_search
-#plan config
-egov.plan.config.host=https://unified-dev.digit.org
-#egov.plan.config.host=http://localhost:8080
-egov.plan.config.endpoint=/plan-service/config/_search
-
#file store
#egov.filestore.host=https://unified-dev.digit.org
egov.filestore.service.host=http://localhost:8084
@@ -64,10 +59,17 @@ egov.filestore.upload.endpoint=/filestore/v1/files
plan.config.consumer.kafka.save.topic=plan-config-create-topic
plan.config.consumer.kafka.update.topic=plan-config-update-topic
-#Plan Create
+#Plan Config
+egov.plan.config.host=https://unified-dev.digit.org
+egov.plan.config.endpoint=/plan-service/config/_search
+
+#Plan
egov.plan.create.endpoint=/plan-service/plan/_create
egov.plan.search.endpoint=/plan-service/plan/_search
+#Plan Facility
+egov.plan.facility.search.endpoint=/plan-service/plan/facility/_search
+
#Campaign Manager
egov.project.factory.search.endpoint=/project-factory/v1/project-type/search
egov.project.factory.update.endpoint=/project-factory/v1/project-type/update
@@ -96,8 +98,8 @@ plan.config.trigger.plan.facility.mappings.status=EXECUTION_TO_BE_DONE
plan.config.update.plan.estimates.into.output.file.status=RESOURCE_ESTIMATIONS_APPROVED
# Pagination config
-resource.default.offset=0
-resource.default.limit=10
+default.offset.for.mdms.data=0
+default.limit.for.mdms.data=10
# Census
egov.census.host=https://unified-dev.digit.org