diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV5Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV5Impl.java index efe199840..2a4635918 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV5Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV5Impl.java @@ -238,7 +238,7 @@ public SBApiResponse readQuestionList(Map requestBody, String au String assessmentIdFromRequest = (String) requestBody.get(Constants.ASSESSMENT_ID_KEY); Map questionsMap = assessUtilServ.readQListfromCache(identifierList,assessmentIdFromRequest,editMode,authUserToken); for (String questionId : identifierList) { - questionList.add(assessUtilServ.filterQuestionMapDetail((Map) questionsMap.get(questionId), + questionList.add(assessUtilServ.filterQuestionMapDetailV2((Map) questionsMap.get(questionId), result.get(Constants.PRIMARY_CATEGORY))); } if (errMsg.isEmpty() && identifierList.size() == questionList.size()) { @@ -481,10 +481,9 @@ private void readSectionLevelParams(Map assessmentAllDetail, } } List> questions = (List>) section.get(Constants.CHILDREN); - int maxQuestions = (int) section.getOrDefault(Constants.MAX_QUESTIONS, questions.size()); - List childNodeList = questions.stream() + List> selectedQuestionsList = processRandomizationForQuestions((Map>) section.get(Constants.SECTION_LEVEL_DEFINITION),questions); + List childNodeList = selectedQuestionsList.stream() .map(question -> (String) question.get(Constants.IDENTIFIER)) - .limit(maxQuestions) .collect(toList()); Collections.shuffle(childNodeList); newSection.put(Constants.CHILD_NODES, childNodeList); @@ -1093,4 +1092,68 @@ public SBApiResponse readAssessmentSavePoint(String assessmentIdentifier, String } return response; } + + + /** + * Process randomization for selecting questions based on section level definitions and limits. + * + * @param sectionLevelDefinitionMap Map containing section level definitions with 'noOfQuestions' and 'noOfMaxQuestions'. + * @param questions List of questions to be processed. + * @return List of selected questions based on randomization and limits. + */ + private List> processRandomizationForQuestions(Map> sectionLevelDefinitionMap, List> questions) { + List> shuffledQuestionsList = shuffleQuestions(questions); + List> selectedQuestionsList = new ArrayList<>(); + Map noOfQuestionsMap = new HashMap<>(); + Map dupNoOfQuestionsMap = new HashMap<>(); // Duplicate map for tracking selected questions + boolean result = sectionLevelDefinitionMap.values().stream() + .anyMatch(proficiencyMap -> { + Object maxNoOfQuestionsValue = proficiencyMap.get(Constants.NO_OF_QUESTIONS); + if (maxNoOfQuestionsValue instanceof Integer) { + return (Integer) maxNoOfQuestionsValue > 0; + } + return false; + }); + + if (!result) { + return questions; + } else { + // Populate noOfQuestionsMap and noOfMaxQuestionsMap from sectionLevelDefinitionMap + sectionLevelDefinitionMap.forEach((sectionLevelDefinitionKey, proficiencyMap) -> proficiencyMap.forEach((key, value) -> { + if (key.equalsIgnoreCase(Constants.NO_OF_QUESTIONS)) { + noOfQuestionsMap.put(sectionLevelDefinitionKey, (Integer) value); + dupNoOfQuestionsMap.put(sectionLevelDefinitionKey,0); + } + })); + + // Process each question for randomization and limit checking + for (Map question : shuffledQuestionsList) { + String questionLevel = (String) question.get(Constants.QUESTION_LEVEL); + // Check if adding one more question of this level is within limits + if (dupNoOfQuestionsMap.getOrDefault(questionLevel, 0) < noOfQuestionsMap.getOrDefault(questionLevel, 0)) { + // Add the question to selected list + selectedQuestionsList.add(question); + // Update dupNoOfQuestionsMap to track the count of selected questions for this level + dupNoOfQuestionsMap.put(questionLevel, dupNoOfQuestionsMap.getOrDefault(questionLevel, 0) + 1); + } + } + return selectedQuestionsList; + } + } + + + + /** + * Shuffles the list of questions maps. + * + * @param questions The list of questions maps to be shuffled. + * @return A new list containing the shuffled questions maps. + */ + public static List> shuffleQuestions(List> questions) { + // Create a copy of the original list to avoid modifying the input list + List> shuffledQnsList = new ArrayList<>(questions); + // Shuffle the list using Collections.shuffle() + Collections.shuffle(shuffledQnsList); + return shuffledQnsList; + } } \ No newline at end of file diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java index 3de59dd36..4b09e0f09 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java @@ -37,4 +37,6 @@ public Map validateQumlAssessment(List originalQuestionL */ public Map validateQumlAssessmentV2(Map questionSetDetailsMap, List originalQuestionList, List> userQuestionList, Map questionMap); + + Map filterQuestionMapDetailV2(Map questionMapResponse, String primaryCategory); } diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java index 600f55675..ca094977f 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java @@ -796,4 +796,41 @@ public static List> shuffleOptions(List> Collections.shuffle(shuffledList); return shuffledList; } + + @Override + public Map filterQuestionMapDetailV2(Map questionMapResponse, + String primaryCategory) { + List questionParams = serverProperties.getAssessmentQuestionParams(); + Map updatedQuestionMap = new HashMap<>(); + for (String questionParam : questionParams) { + if (questionMapResponse.containsKey(questionParam)) { + updatedQuestionMap.put(questionParam, questionMapResponse.get(questionParam)); + } + } + if (questionMapResponse.containsKey(Constants.EDITOR_STATE) + && primaryCategory.equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + Map editorState = (Map) questionMapResponse.get(Constants.EDITOR_STATE); + updatedQuestionMap.put(Constants.EDITOR_STATE, editorState); + } + if (questionMapResponse.containsKey(Constants.CHOICES) + && updatedQuestionMap.containsKey(Constants.PRIMARY_CATEGORY)) { + Map choicesObj = (Map) questionMapResponse.get(Constants.CHOICES); + Map updatedChoicesMap = new HashMap<>(); + if (choicesObj.containsKey(Constants.OPTIONS)) { + List> optionsMapList = (List>) choicesObj + .get(Constants.OPTIONS); + updatedChoicesMap.put(Constants.OPTIONS, shuffleOptions(optionsMapList)); + } + updatedQuestionMap.put(Constants.CHOICES, updatedChoicesMap); + } + if (questionMapResponse.containsKey(Constants.RHS_CHOICES) + && updatedQuestionMap.containsKey(Constants.PRIMARY_CATEGORY) && updatedQuestionMap + .get(Constants.PRIMARY_CATEGORY).toString().equalsIgnoreCase(Constants.MTF_QUESTION)) { + List rhsChoicesObj = (List) questionMapResponse.get(Constants.RHS_CHOICES); + Collections.shuffle(rhsChoicesObj); + updatedQuestionMap.put(Constants.RHS_CHOICES, rhsChoicesObj); + } + + return updatedQuestionMap; + } } \ No newline at end of file diff --git a/src/main/java/org/sunbird/common/util/Constants.java b/src/main/java/org/sunbird/common/util/Constants.java index 881de6690..6a652ce77 100644 --- a/src/main/java/org/sunbird/common/util/Constants.java +++ b/src/main/java/org/sunbird/common/util/Constants.java @@ -1103,8 +1103,36 @@ public class Constants { public static final String TABLE_TOP_10_LEARNER ="mdo_top_learners"; public static final String CSV_FILE = ".csv"; public static final String XLSX_FILE = ".xlsx"; - - private Constants() { + public static final String NO_OF_QUESTIONS = "noOfQuestions"; + public static final String CADRE_DETAILS = "cadreDetails"; + public static final String CADRE_CONFIG = "cadreConfig"; + public static final String CIVIL_SERVICE_TYPE = "civilServiceType"; + public static final String SERVICE_TYPE = "serviceList"; + public static final String CADRE_BATCH = "cadreBatch"; + public static final String CONTROLLING_AUTHORITY = "cadreControllingAuthorityName"; + public static final String CADRE_NAME = "cadreName"; + public static final String CIVIL_SERVICE_NAME = "civilServiceName"; + public static final String CADRE_LIST = "cadreList"; + public static final String CIVIL_SERVICE_TYPE_ID = "civilServiceTypeId"; + public static final String CIVIL_SERVICE_ID = "civilServiceId"; + public static final String CADRE_ID = "cadreId"; + public static final String CADRE_BATCH_START_YR = "startBatchYear"; + public static final String CADRE_BATCH_END_YR = "endBatchYear"; + public static final String CADRE_BATCH_EXCLUSION_YR = "exculsionYearList"; + public static final String NLW_USER_LEADERBOARD = "nlw_user_leaderboard"; + public static final String NLW_MDO_LEADERBOARD= "nlw_mdo_leaderboard"; + public static final String API_HALL_OF_FAME_ORG_READ = "api.v1.halloffame.org.read"; + public static final String API_HALL_OF_FAME_MDO_LEADERBOARD = "api.v1.halloffame.mdoleaderboard"; + public static final String USER_LEADERBOARD = "userLeaderBoard"; + public static final String SIZE = "size"; + public static final String INVALID_ORG_ID = "invalid organisation id"; + public static final String MDO_LEADERBOARD = "mdoLeaderBoard"; + public static final String NO_DATA_FOUND = "no data found"; + public static final String NO_DATA_FOUND_FOR_THE_ORGANISATION = "no data found for the organisation"; + public static final String ERROR_WHILE_PROCESSING_USER_LEADERBOARD = "error while processing userLeaderBoard"; + public static final String ERROR_WHILE_PROCESSING_MDO_LEADERBOARD = "error while processing mdoLeaderBoard"; + + private Constants() { throw new IllegalStateException("Utility class"); } diff --git a/src/main/java/org/sunbird/halloffame/controller/HallOfFameController.java b/src/main/java/org/sunbird/halloffame/controller/HallOfFameController.java index 8410e5f81..e4f4a4512 100644 --- a/src/main/java/org/sunbird/halloffame/controller/HallOfFameController.java +++ b/src/main/java/org/sunbird/halloffame/controller/HallOfFameController.java @@ -39,4 +39,16 @@ public ResponseEntity> fetchHallOfFameData() { SBApiResponse response = hallOfFameService.fetchingTop10Learners(ministryOrgId, authToken); return new ResponseEntity<>(response, response.getResponseCode()); } + + @GetMapping("v1/halloffame/org/read/{orgId}") + public ResponseEntity getUserLeaderBoard(@PathVariable String orgId) { + SBApiResponse response = hallOfFameService.getUserLeaderBoard(orgId); + return new ResponseEntity<>(response, response.getResponseCode()); + } + + @GetMapping("v1/halloffame/mdoleaderboard") + public ResponseEntity getMdoLeaderBoard() { + SBApiResponse response = hallOfFameService.getMdoLeaderBoard(); + return new ResponseEntity<>(response, response.getResponseCode()); + } } diff --git a/src/main/java/org/sunbird/halloffame/service/HallOfFameService.java b/src/main/java/org/sunbird/halloffame/service/HallOfFameService.java index 4acc51ad1..5fc7aca12 100644 --- a/src/main/java/org/sunbird/halloffame/service/HallOfFameService.java +++ b/src/main/java/org/sunbird/halloffame/service/HallOfFameService.java @@ -13,4 +13,9 @@ public interface HallOfFameService { public SBApiResponse learnerLeaderBoard(String rootOrgId, String authToken); public SBApiResponse fetchingTop10Learners(String ministryOrgId, String authToken); + + public SBApiResponse getUserLeaderBoard(String OrgId); + + public SBApiResponse getMdoLeaderBoard(); + } diff --git a/src/main/java/org/sunbird/halloffame/service/HallOfFameServiceImpl.java b/src/main/java/org/sunbird/halloffame/service/HallOfFameServiceImpl.java index b11020112..504aa8cdd 100644 --- a/src/main/java/org/sunbird/halloffame/service/HallOfFameServiceImpl.java +++ b/src/main/java/org/sunbird/halloffame/service/HallOfFameServiceImpl.java @@ -146,6 +146,63 @@ public SBApiResponse fetchingTop10Learners(String ministryOrgId, String authToke return response; } + @Override + public SBApiResponse getUserLeaderBoard(String orgId) { + SBApiResponse response = ProjectUtil.createDefaultResponse(Constants.API_HALL_OF_FAME_ORG_READ); + if (StringUtils.isBlank(orgId)) { + setBadRequestResponse(response, Constants.INVALID_ORG_ID); + return response; + } + Map propertyMap = new HashMap<>(); + propertyMap.put(Constants.ORGID, orgId); + propertyMap.put(Constants.DB_COLUMN_ROW_NUM, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + try { + List> userLeaderBoard = cassandraOperation.getRecordsByPropertiesWithoutFiltering( + Constants.KEYSPACE_SUNBIRD, Constants.NLW_USER_LEADERBOARD, propertyMap, null); + if (CollectionUtils.isEmpty(userLeaderBoard)) { + response.getParams().setErrmsg(Constants.NO_DATA_FOUND_FOR_THE_ORGANISATION); + response.getParams().setStatus(Constants.SUCCESS); + response.setResponseCode(HttpStatus.OK); + } else { + response.getParams().setStatus(Constants.SUCCESS); + response.put(Constants.USER_LEADERBOARD, userLeaderBoard); + response.setResponseCode(HttpStatus.OK); + } + } catch (Exception e) { + response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); + response.getParams().setErrmsg(Constants.ERROR_WHILE_PROCESSING_USER_LEADERBOARD); + response.getParams().setStatus(Constants.FAILED); + logger.error("failed to process userLeaderBoard :: {}", String.valueOf(e)); + } + return response; + } + + @Override + public SBApiResponse getMdoLeaderBoard() { + SBApiResponse response = ProjectUtil.createDefaultResponse(Constants.API_HALL_OF_FAME_MDO_LEADERBOARD); + Map propertyMap = new HashMap<>(); + propertyMap.put(Constants.SIZE, Arrays.asList("S", "M", "L", "XL")); + try { + List> mdoLeaderBoard = cassandraOperation.getRecordsByPropertiesWithoutFiltering( + Constants.KEYSPACE_SUNBIRD, Constants.NLW_MDO_LEADERBOARD, propertyMap, null); + if (CollectionUtils.isEmpty(mdoLeaderBoard)) { + response.getParams().setErrmsg(Constants.NO_DATA_FOUND); + response.getParams().setStatus(Constants.SUCCESS); + response.setResponseCode(HttpStatus.OK); + } else { + response.getParams().setStatus(Constants.SUCCESS); + response.put(Constants.MDO_LEADERBOARD, mdoLeaderBoard); + response.setResponseCode(HttpStatus.OK); + } + } catch (Exception e) { + response.getParams().setErrmsg(Constants.ERROR_WHILE_PROCESSING_MDO_LEADERBOARD); + response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); + response.getParams().setStatus(Constants.FAILED); + logger.error("failed to process mdoLeaderBoard :: {}", String.valueOf(e)); + } + return response; + } + private void setBadRequestResponse(SBApiResponse response, String errMsg) { response.getParams().setStatus(Constants.FAILED); response.getParams().setErrmsg(errMsg); diff --git a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java index d3dc31867..c6e91c0e2 100644 --- a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java +++ b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; @@ -159,6 +160,7 @@ public SBApiResponse profileUpdate(Map request, String userToken headerValues.put(Constants.X_AUTH_TOKEN, userToken); Map workflowResponse = new HashMap<>(); Map updateResponse = new HashMap<>(); + String validateCadreDetails = ""; if (!profileDetailsMap.isEmpty()) { List listOfChangedDetails = new ArrayList<>(); for (String keys : profileDetailsMap.keySet()) { @@ -184,11 +186,19 @@ public SBApiResponse profileUpdate(Map request, String userToken existingProfileDetails.put(changedObj, profileDetailsMap.get(changedObj)); } } - // Additional Condition for updating personal Details directly to user object if (Constants.PERSONAL_DETAILS.equalsIgnoreCase(changedObj)) { getModifiedPersonalDetails(profileDetailsMap.get(changedObj), requestData); } + if (Constants.CADRE_DETAILS.equalsIgnoreCase(changedObj)){ + String cadreErrMsg = validateCadreDetails(profileDetailsMap); + if (!cadreErrMsg.isEmpty()) { + response.setResponseCode(HttpStatus.BAD_REQUEST); + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(cadreErrMsg); + return response; + } + } } //This field is updated via approval /*if (validateJsonAgainstSchema(existingProfileDetails)) { @@ -338,6 +348,122 @@ public SBApiResponse profileUpdate(Map request, String userToken return response; } + private String validateCadreDetails(Map profileDetailsMap) { + Map cadreDetail; + try { + Map cadreMap = (Map) profileDetailsMap.get( + Constants.CADRE_DETAILS); + if (MapUtils.isEmpty(cadreMap)) { + return "Failed to validate cadreDetails schema"; + } + List mandatoryCaderAttributes = Arrays.asList( + Constants.CIVIL_SERVICE_TYPE_ID, + Constants.CIVIL_SERVICE_TYPE, + Constants.CIVIL_SERVICE_ID, + Constants.CIVIL_SERVICE_NAME, + Constants.CADRE_ID, + Constants.CADRE_NAME, + Constants.CONTROLLING_AUTHORITY + ); + + List missingAttribts = new ArrayList(); + for (String field : mandatoryCaderAttributes) { + if (!cadreMap.containsKey(field) || cadreMap.get(field) == null || + (cadreMap.get(field) instanceof String && ((String) cadreMap.get(field)).isEmpty())) { + missingAttribts.add(field); + } + } + if (!cadreMap.containsKey(Constants.CADRE_BATCH) + || cadreMap.get(Constants.CADRE_BATCH) == null || + !(cadreMap.get(Constants.CADRE_BATCH) instanceof Integer)) { + missingAttribts.add(Constants.CADRE_BATCH); + } + + if (missingAttribts.size() > 0) { + return "Missing mandatory attributes or Improper dataType. " + missingAttribts.toString(); + } + + Map propertyMap = new HashMap<>(); + propertyMap.put(Constants.ID, Constants.CADRE_CONFIG); + cadreDetail = cassandraOperation.getRecordsByPropertiesWithoutFiltering( + Constants.KEYSPACE_SUNBIRD, Constants.TABLE_SYSTEM_SETTINGS, propertyMap, + null, 2).get(0); + if (cadreDetail.isEmpty() && !cadreDetail.containsKey(Constants.VALUE)) { + return "Failed to validate cadreDetails schema"; + } + boolean isCivilTypePresent = false; + boolean isServiceTypePresent = false; + boolean isCadrePresent = false; + boolean isValidBatchYr = false; + + String mapString = (String) cadreDetail.get(Constants.VALUE); + Map map = mapper.readValue(mapString, + new TypeReference>() { + }); + + Map civilServiceMap = (Map) map.get( + Constants.CIVIL_SERVICE_TYPE); + List> civilServiceList = (List>) civilServiceMap.get( + "civilServiceTypeList"); + + for (Map eachCivilService : civilServiceList) { + String civilServiceName = (String) eachCivilService.get(Constants.NAME); + if (((String) cadreMap.get(Constants.CIVIL_SERVICE_TYPE)).equalsIgnoreCase( + civilServiceName)) { + isCivilTypePresent = true; + List> serviceMap = (List>) eachCivilService.get( + Constants.SERVICE_TYPE); + + for (Map service : serviceMap) { + String serviceTypeName = (String) service.get(Constants.NAME); + if (((String) cadreMap.get(Constants.CIVIL_SERVICE_NAME)).equalsIgnoreCase( + serviceTypeName)) { + isServiceTypePresent = true; + List> cadreList = (List>) service.get( + Constants.CADRE_LIST); + + Optional> matchingCadre = cadreList.stream() + .filter(eachCadre -> + ((String) cadreMap.get(Constants.CADRE_NAME)) + .equalsIgnoreCase((String) eachCadre.get(Constants.NAME))) + .findFirst(); + + if (matchingCadre.isPresent()) { + isCadrePresent = true; + Map eachCadre = matchingCadre.get(); + int cadreBatch = (int) cadreMap.get(Constants.CADRE_BATCH); + List exclusionYearList = (List) eachCadre.get( + Constants.CADRE_BATCH_EXCLUSION_YR); + isValidBatchYr = (cadreBatch >= (int) eachCadre.get(Constants.CADRE_BATCH_START_YR) + && cadreBatch <= (int) eachCadre.get(Constants.CADRE_BATCH_END_YR)) + && !exclusionYearList.contains(cadreBatch); + } + + } + } + } + } + + if (!isServiceTypePresent) { + return "Invalid civilserviceType is given"; + } + if (!isCivilTypePresent) { + return "Invalid civilSerivceName is given"; + } + if (!isCadrePresent) { + return "Invalid cadreName is given"; + } + if (!isValidBatchYr) { + return "Invalid cadreBatch is given"; + } + + return ""; + } catch (Exception e) { + log.error("Failed to validate CadreDetails. Exception :" + e.getMessage()); + } + return ""; + } + public boolean validateJsonAgainstSchema(Map existingProfileDetails) { try { String jsonData = new Gson().toJson(existingProfileDetails);