From b7385c750f5a9882c1773679f456c448fea97f68 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 11 Nov 2022 11:44:05 +0530 Subject: [PATCH 01/13] Test commit for Assessment feature (#148) --- .../assessment/repo/AssessmentRepository.java | 9 +- .../repo/AssessmentRepositoryImpl.java | 67 +- .../service/AssessmentServiceImpl.java | 59 +- .../service/AssessmentServiceV2Impl.java | 1020 +++++++++++------ .../service/AssessmentUtilServiceV2.java | 4 + .../service/AssessmentUtilServiceV2Impl.java | 273 +++-- .../java/org/sunbird/cache/RedisCacheMgr.java | 122 -- .../controller/RedisCacheController.java | 34 - .../cache/service/RedisCacheService.java | 13 - .../cache/service/RedisCacheServiceImpl.java | 76 -- .../common/util/AccessTokenValidator.java | 80 -- .../common/util/CbExtServerProperties.java | 44 + .../org/sunbird/common/util/Constants.java | 41 +- .../util/KeyCloakConnectionProvider.java | 151 --- .../org/sunbird/common/util/ProjectUtil.java | 3 - .../common/util/RequestInterceptor.java | 109 +- .../core/config/ConsumerConfiguration.java | 1 - .../org/sunbird/core/config/RedisConfig.java | 42 - .../service/ExploreCourseServiceImpl.java | 6 - .../profile/service/ProfileServiceImpl.java | 80 +- .../searchby/service/SearchByService.java | 50 +- .../service/UserRegistrationServiceImpl.java | 18 +- src/main/resources/application.properties | 5 +- 23 files changed, 1135 insertions(+), 1172 deletions(-) delete mode 100644 src/main/java/org/sunbird/cache/RedisCacheMgr.java delete mode 100644 src/main/java/org/sunbird/cache/controller/RedisCacheController.java delete mode 100644 src/main/java/org/sunbird/cache/service/RedisCacheService.java delete mode 100644 src/main/java/org/sunbird/cache/service/RedisCacheServiceImpl.java delete mode 100644 src/main/java/org/sunbird/common/util/AccessTokenValidator.java delete mode 100644 src/main/java/org/sunbird/common/util/KeyCloakConnectionProvider.java delete mode 100644 src/main/java/org/sunbird/core/config/RedisConfig.java diff --git a/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java b/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java index 9895962df..d89784878 100644 --- a/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java +++ b/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java @@ -49,7 +49,12 @@ public Map insertQuizOrAssessment(Map persist, B public List> getAssessmentbyContentUser(String rootOrg, String courseId, String userId) throws Exception; - boolean addUserAssesmentStartTime(String userId, String assessmentIdentifier, Timestamp startTime); + List> fetchUserAssessmentDataFromDB(String userId, String assessmentIdentifier); - Date fetchUserAssessmentStartTime(String userId, String s); + boolean addUserAssesmentDataToDB(String userId, String assessmentId, Timestamp startTime, Timestamp endTime, + Map questionSet, String status); + + Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdentifier, + Map submitAssessmentRequest, Map submitAssessmentResponse, String status, + Date startTime); } diff --git a/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java b/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java index f3575b006..66f75fc49 100644 --- a/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java +++ b/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java @@ -1,6 +1,14 @@ package org.sunbird.assessment.repo; -import com.datastax.driver.core.utils.UUIDs; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.sunbird.assessment.dto.AssessmentSubmissionDTO; @@ -9,10 +17,8 @@ import org.sunbird.common.util.Constants; import org.sunbird.core.logger.CbExtLogger; -import java.math.BigDecimal; -import java.sql.Timestamp; -import java.text.SimpleDateFormat; -import java.util.*; +import com.datastax.driver.core.utils.UUIDs; +import com.google.gson.Gson; @Service public class AssessmentRepositoryImpl implements AssessmentRepository { @@ -22,7 +28,7 @@ public class AssessmentRepositoryImpl implements AssessmentRepository { public static final String SOURCE_ID = "sourceId"; public static final String USER_ID = "userId"; private CbExtLogger logger = new CbExtLogger(getClass().getName()); - + @Autowired UserAssessmentSummaryRepository userAssessmentSummaryRepo; @@ -128,23 +134,50 @@ public List> getAssessmentbyContentUser(String rootOrg, Stri } @Override - public boolean addUserAssesmentStartTime(String userId, String assessmentIdentifier, Timestamp startTime) { + public boolean addUserAssesmentDataToDB(String userId, String assessmentIdentifier, Timestamp startTime, + Timestamp endTime, Map questionSet, String status) { Map request = new HashMap<>(); request.put(Constants.USER_ID, userId); - request.put(Constants.IDENTIFIER, assessmentIdentifier); - cassandraOperation.deleteRecord(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_USER_ASSESSMENT_TIME, request); - request.put("starttime", startTime); - SBApiResponse resp = cassandraOperation.insertRecord(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_USER_ASSESSMENT_TIME, request); + request.put(Constants.ASSESSMENT_ID_KEY, assessmentIdentifier); + request.put(Constants.START_TIME, startTime); + request.put(Constants.END_TIME, endTime); + request.put(Constants.ASSESSMENT_READ_RESPONSE, new Gson().toJson(questionSet)); + request.put(Constants.STATUS, status); + SBApiResponse resp = cassandraOperation.insertRecord(Constants.KEYSPACE_SUNBIRD, + Constants.TABLE_USER_ASSESSMENT_DATA, request); return resp.get(Constants.RESPONSE).equals(Constants.SUCCESS); } - @Override - public Date fetchUserAssessmentStartTime(String userId, String assessmentIdentifier) { + @Override + public List> fetchUserAssessmentDataFromDB(String userId, String assessmentIdentifier) { Map request = new HashMap<>(); request.put(Constants.USER_ID, userId); - request.put(Constants.IDENTIFIER, assessmentIdentifier); - Map existingDataList = cassandraOperation.getRecordsByProperties(Constants.KEYSPACE_SUNBIRD, - Constants.TABLE_USER_ASSESSMENT_TIME, request, null).get(0); - return (Date) existingDataList.get("starttime"); + request.put(Constants.ASSESSMENT_ID_KEY, assessmentIdentifier); + List> existingDataList = cassandraOperation.getRecordsByProperties( + Constants.KEYSPACE_SUNBIRD, Constants.TABLE_USER_ASSESSMENT_DATA, request, null); + return existingDataList; + } + + @Override + public Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdentifier, + Map submitAssessmentRequest, Map submitAssessmentResponse, String status, + Date startTime) { + Map compositeKeys = new HashMap<>(); + compositeKeys.put(Constants.USER_ID, userId); + compositeKeys.put(Constants.ASSESSMENT_ID_KEY, assessmentIdentifier); + compositeKeys.put(Constants.START_TIME, startTime); + Map fieldsToBeUpdated = new HashMap<>(); + if (!submitAssessmentRequest.isEmpty()) { + fieldsToBeUpdated.put("submitassessmentrequest", new Gson().toJson(submitAssessmentRequest)); + } + if (!submitAssessmentResponse.isEmpty()) { + fieldsToBeUpdated.put("submitassessmentresponse", new Gson().toJson(submitAssessmentResponse)); + } + if (!status.isEmpty()) { + fieldsToBeUpdated.put(Constants.STATUS, status); + } + cassandraOperation.updateRecord(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_USER_ASSESSMENT_DATA, + fieldsToBeUpdated, compositeKeys); + return true; } } diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceImpl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceImpl.java index dc462feaa..47745b74a 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceImpl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceImpl.java @@ -10,11 +10,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; import org.sunbird.assessment.dto.AssessmentSubmissionDTO; import org.sunbird.assessment.model.QuestionSet; import org.sunbird.assessment.repo.AssessmentRepository; -import org.sunbird.cache.RedisCacheMgr; import org.sunbird.common.model.SunbirdApiHierarchyResultContent; import org.sunbird.common.model.SunbirdApiResp; import org.sunbird.common.service.ContentService; @@ -57,9 +55,6 @@ public class AssessmentServiceImpl implements AssessmentService { @Autowired CbExtServerProperties extServerProperties; - @Autowired - RedisCacheMgr redisCacheMgr; - @Override public Map submitAssessment(String rootOrg, AssessmentSubmissionDTO data, String userId) throws Exception { @@ -226,42 +221,28 @@ private List> getAssessments(List> resul public Map getAssessmentContent(String courseId, String assessmentContentId) { Map result = new HashMap<>(); try { - Object assessmentQuestionSet = redisCacheMgr.getCache(Constants.ASSESSMENT_QNS_SET + assessmentContentId); - - if (ObjectUtils.isEmpty(assessmentQuestionSet)) { - String serviceURL = extServerProperties.getKmBaseHost() - + extServerProperties.getContentHierarchyDetailEndPoint(); - serviceURL = (serviceURL.replace("{courseId}", courseId)).replace("{hierarchyType}", "detail"); - SunbirdApiResp response = mapper.convertValue( - outboundRequestHandlerService.fetchUsingGetWithHeaders(serviceURL, new HashMap<>()), - SunbirdApiResp.class); - - if (response.getResponseCode().equalsIgnoreCase("Ok")) { - // get course content - List children = response.getResult().getContent().getChildren(); - for (SunbirdApiHierarchyResultContent child : children) { - // get assessment content with id - if (child.getIdentifier().equals(assessmentContentId) - && child.getArtifactUrl().endsWith(".json")) { - // read assessment json file - QuestionSet assessmentContent = mapper.convertValue(outboundRequestHandlerService - .fetchUsingGetWithHeaders(child.getArtifactUrl(), new HashMap<>()), - QuestionSet.class); - - QuestionSet assessmentQnsSet = assessUtilServ.removeAssessmentAnsKey(assessmentContent); - result.put(Constants.STATUS, Constants.SUCCESSFUL); - result.put(Constants.QUESTION_SET, assessmentQnsSet); - // cache the response - redisCacheMgr.putCache(Constants.ASSESSMENT_QNS_ANS_SET + assessmentContentId, - assessmentContent); - redisCacheMgr.putCache(Constants.ASSESSMENT_QNS_SET + assessmentContentId, assessmentQnsSet); - } + String serviceURL = extServerProperties.getKmBaseHost() + + extServerProperties.getContentHierarchyDetailEndPoint(); + serviceURL = (serviceURL.replace("{courseId}", courseId)).replace("{hierarchyType}", "detail"); + SunbirdApiResp response = mapper.convertValue( + outboundRequestHandlerService.fetchUsingGetWithHeaders(serviceURL, new HashMap<>()), + SunbirdApiResp.class); + + if (response.getResponseCode().equalsIgnoreCase("Ok")) { + // get course content + List children = response.getResult().getContent().getChildren(); + for (SunbirdApiHierarchyResultContent child : children) { + // get assessment content with id + if (child.getIdentifier().equals(assessmentContentId) && child.getArtifactUrl().endsWith(".json")) { + // read assessment json file + QuestionSet assessmentContent = mapper.convertValue(outboundRequestHandlerService + .fetchUsingGetWithHeaders(child.getArtifactUrl(), new HashMap<>()), QuestionSet.class); + + QuestionSet assessmentQnsSet = assessUtilServ.removeAssessmentAnsKey(assessmentContent); + result.put(Constants.STATUS, Constants.SUCCESSFUL); + result.put(Constants.QUESTION_SET, assessmentQnsSet); } } - - } else { - result.put(Constants.STATUS, Constants.SUCCESSFUL); - result.put(Constants.QUESTION_SET, assessmentQuestionSet); } return result; diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java index df6a22f48..1b2cceaae 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java @@ -1,399 +1,655 @@ package org.sunbird.assessment.service; -import com.fasterxml.jackson.databind.ObjectMapper; +import static java.util.stream.Collectors.toList; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.joda.time.DateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; import org.sunbird.assessment.repo.AssessmentRepository; -import org.sunbird.cache.RedisCacheMgr; import org.sunbird.common.model.SBApiResponse; -import org.sunbird.common.model.SunbirdApiResp; -import org.sunbird.common.service.OutboundRequestHandlerServiceImpl; import org.sunbird.common.util.CbExtServerProperties; import org.sunbird.common.util.Constants; import org.sunbird.common.util.RequestInterceptor; -import org.sunbird.core.exception.ApplicationLogicError; -import org.sunbird.core.logger.CbExtLogger; +import org.sunbird.core.producer.Producer; -import java.sql.Timestamp; -import java.util.*; -import java.util.stream.Collectors; +import com.beust.jcommander.internal.Lists; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; @Service @SuppressWarnings("unchecked") public class AssessmentServiceV2Impl implements AssessmentServiceV2 { - private final CbExtLogger logger = new CbExtLogger(getClass().getName()); - - private final ObjectMapper mapper = new ObjectMapper(); - - @Autowired - AssessmentUtilServiceV2 assessUtilServ; - - @Autowired - RedisCacheMgr redisCacheMgr; - - @Autowired - CbExtServerProperties cbExtServerProperties; - - @Autowired - OutboundRequestHandlerServiceImpl outboundRequestHandlerService; - - @Autowired - AssessmentRepository assessmentRepository; - - public SBApiResponse readAssessment(String assessmentIdentifier, String token) throws Exception { - SBApiResponse response = new SBApiResponse(); - try { - String userId = RequestInterceptor.fetchUserIdFromAccessToken(token); - if (userId != null) { - Map assessmentAllDetail = (Map) redisCacheMgr - .getCache(Constants.ASSESSMENT_ID + assessmentIdentifier); - boolean isSuccess = true; - if (ObjectUtils.isEmpty(assessmentAllDetail)) { - Map hierarcyReadApiResponse = getReadHierarchyApiResponse(assessmentIdentifier, token); - if (!Constants.OK.equalsIgnoreCase((String) hierarcyReadApiResponse.get(Constants.RESPONSE_CODE))) { - isSuccess = false; - } else { - assessmentAllDetail = (Map) ((Map) hierarcyReadApiResponse - .get(Constants.RESULT)).get(Constants.QUESTION_SET); - redisCacheMgr.putCache(Constants.ASSESSMENT_ID + assessmentIdentifier, assessmentAllDetail); - } - } - response = prepareAssessmentResponse(assessmentAllDetail, isSuccess); - redisCacheMgr.putCache(Constants.USER_ASSESS_REQ + token, response.getResult().get(Constants.QUESTION_SET)); - if (assessmentAllDetail.get(Constants.DURATION) != null) { - boolean resp = assessmentRepository.addUserAssesmentStartTime(userId, Constants.ASSESSMENT_ID + assessmentIdentifier, new Timestamp(new Date().getTime())); - return response; - } - } - } catch (Exception e) { - logger.error(e); - throw new ApplicationLogicError("REQUEST_COULD_NOT_BE_PROCESSED", e); - } - return response; - } - - public SBApiResponse readQuestionList(Map requestBody, String authUserToken) throws Exception { - try { - List identifierList = getQuestionIdList(requestBody); - List questionList = new ArrayList<>(); - List newIdentifierList = new ArrayList<>(); - List map = redisCacheMgr.mget(identifierList); - int size = map.size(); - for (int i = 0; i < map.size(); i++) { - if (ObjectUtils.isEmpty(map.get(i))) { - newIdentifierList.add(identifierList.get(i)); - } else { - questionList.add(filterQuestionMapDetail((Map) map.get(i))); - } - } - if (newIdentifierList.size() > 0) { - Map questionMapResponse = readQuestionDetails(newIdentifierList); - if (questionMapResponse != null && Constants.OK.equalsIgnoreCase((String) questionMapResponse.get(Constants.RESPONSE_CODE))) { - List> questionMap = ((List>) ((Map) questionMapResponse - .get(Constants.RESULT)).get(Constants.QUESTIONS)); - for (Map qmap : questionMap) { - if (!ObjectUtils.isEmpty(questionMap)) { - redisCacheMgr.putCache(Constants.QUESTION_ID + qmap.get(Constants.IDENTIFIER), qmap); - questionList.add(filterQuestionMapDetail(qmap)); - } else { - logger.error(new Exception("Failed to get Question Details for Id: " + qmap.get(Constants.IDENTIFIER))); - } - } - } - } - return prepareQuestionResponse(questionList, questionList.size() > 0); - } catch (Exception e) { - logger.error(e); - throw new ApplicationLogicError("REQUEST_COULD_NOT_BE_PROCESSED", e); - } - - } - - @Override - public SBApiResponse submitAssessment(Map data, String authUserToken) throws Exception { - SBApiResponse outgoingResponse = new SBApiResponse(); - String assessmentId = (String) data.get(Constants.IDENTIFIER); - Map assessmentHierarchy = (Map) redisCacheMgr - .getCache(Constants.ASSESSMENT_ID + assessmentId); - // logger.info("Submit Assessment: userId: " + userId + ", data: " + - // data.toString()); - // Check User exists - // if (!userUtilService.validateUser(userId)) { - // throw new BadRequestException("Invalid UserId."); - // } - String userId = RequestInterceptor.fetchUserIdFromAccessToken(authUserToken); - if (userId != null) { - Date assessmentStartTime = assessmentRepository.fetchUserAssessmentStartTime(userId, Constants.ASSESSMENT_ID + assessmentId); - if (assessmentStartTime != null) { - Timestamp submissionTime = new Timestamp(new Date().getTime()); - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(new Timestamp(assessmentStartTime.getTime()).getTime()); - cal.add(Calendar.SECOND, Integer.valueOf((String) assessmentHierarchy.get(Constants.DURATION)).intValue() + Integer.valueOf(cbExtServerProperties.getUserAssessmentSubmissionDuration())); - Timestamp later = new Timestamp(cal.getTime().getTime()); - int time = submissionTime.compareTo(later); - if (time <= 0) { - outgoingResponse.setResponseCode(HttpStatus.OK); - outgoingResponse.getResult().put(Constants.IDENTIFIER, assessmentId); - outgoingResponse.getResult().put(Constants.OBJECT_TYPE, assessmentHierarchy.get(Constants.OBJECT_TYPE)); - outgoingResponse.getResult().put(Constants.PRIMARY_CATEGORY, assessmentHierarchy.get(Constants.PRIMARY_CATEGORY)); - - // Check Sections are available - if (data.containsKey(Constants.CHILDREN) - && !CollectionUtils.isEmpty((List>) data.get(Constants.CHILDREN))) { - List> sectionList = (List>) data.get(Constants.CHILDREN); - - for (Map section : sectionList) { - String id = (String) section.get(Constants.IDENTIFIER); - String scoreCutOffType = ((String) section.get(Constants.SCORE_CUTOFF_TYPE)).toLowerCase(); - switch (scoreCutOffType) { - case Constants.ASSESSMENT_LEVEL_SCORE_CUTOFF: { - if (sectionList.size() > 1) { - // There should be only one section -- if not -- throw error - } - validateAssessmentLevelScore(outgoingResponse, section, assessmentHierarchy); - } - break; - case Constants.SECTION_LEVEL_SCORE_CUTOFF: { - } - break; - default: - break; - } - } - } else { - // TODO - // At least one section details should be available in the submit request... - // throw error if no section details. - } - } - } - } - return outgoingResponse; - } - - private Map getReadHierarchyApiResponse(String assessmentIdentifier, String token) { - try { - StringBuilder sbUrl = new StringBuilder(cbExtServerProperties.getAssessmentHost()); - sbUrl.append(cbExtServerProperties.getAssessmentHierarchyReadPath()); - String serviceURL = sbUrl.toString().replace(Constants.IDENTIFIER_REPLACER, assessmentIdentifier); - Map headers = new HashMap<>(); - headers.put(Constants.X_AUTH_TOKEN, token); - headers.put(Constants.AUTHORIZATION, cbExtServerProperties.getSbApiKey()); - Object o = outboundRequestHandlerService.fetchUsingGetWithHeaders(serviceURL, headers); - return mapper.convertValue(o, Map.class); - } catch (Exception e) { - logger.error(e); - throw new ApplicationLogicError(e.getMessage()); - } - } - - private SBApiResponse prepareAssessmentResponse(Map hierarchyResponse, boolean isSuccess) { - SBApiResponse outgoingResponse = new SBApiResponse(); - outgoingResponse.setId(Constants.API_QUESTIONSET_HIERARCHY_GET); - outgoingResponse.setVer(Constants.VER); - outgoingResponse.getParams().setResmsgid(UUID.randomUUID().toString()); - if (isSuccess) { - outgoingResponse.getParams().setStatus(Constants.SUCCESS); - outgoingResponse.setResponseCode(HttpStatus.OK); - readAssessmentLevelData(hierarchyResponse, outgoingResponse); - } else { - outgoingResponse.getParams().setStatus(Constants.FAILED); - outgoingResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); - } - return outgoingResponse; - } - - private void readAssessmentLevelData(Map assessmentAllDetail, SBApiResponse outgoingResponse) { - List assessmentParams = cbExtServerProperties.getAssessmentLevelParams(); - Map assessmentFilteredDetail = new HashMap<>(); - for (String assessmentParam : assessmentParams) { - if ((assessmentAllDetail.containsKey(assessmentParam))) { - assessmentFilteredDetail.put(assessmentParam, assessmentAllDetail.get(assessmentParam)); - } - } - readSectionLevelParams(assessmentAllDetail, assessmentFilteredDetail); - outgoingResponse.getResult().put(Constants.QUESTION_SET, assessmentFilteredDetail); - } - - private void readSectionLevelParams(Map assessmentAllDetail, - Map assessmentFilteredDetail) { - List> sectionResponse = new ArrayList<>(); - List sectionParams = cbExtServerProperties.getAssessmentSectionParams(); - List> sections = (List>) assessmentAllDetail.get(Constants.CHILDREN); - List sectionIdList = new ArrayList(); - for (Map section : sections) { - sectionIdList.add((String) section.get(Constants.IDENTIFIER)); - Map newSection = new HashMap<>(); - for (String sectionParam : sectionParams) { - if (section.containsKey(sectionParam)) { - newSection.put(sectionParam, section.get(sectionParam)); - } - } - List allQuestionIdList = new ArrayList(); - List> questions = (List>) section.get(Constants.CHILDREN); - for (Map question : questions) { - allQuestionIdList.add((String) question.get(Constants.IDENTIFIER)); - } - Collections.shuffle(allQuestionIdList); - List childNodeList = new ArrayList<>(); - if (!ObjectUtils.isEmpty(section.get(Constants.MAX_QUESTIONS))) { - int maxQuestions = (int) section.get(Constants.MAX_QUESTIONS); - childNodeList = allQuestionIdList.stream().limit(maxQuestions).collect(Collectors.toList()); - } - newSection.put(Constants.CHILD_NODES, childNodeList); - sectionResponse.add(newSection); - } - assessmentFilteredDetail.put(Constants.CHILDREN, sectionResponse); - assessmentFilteredDetail.put(Constants.CHILD_NODES, sectionIdList); - } - - private List getQuestionIdList(Map questionListRequest) throws Exception { - if (questionListRequest.containsKey(Constants.REQUEST)) { - Map request = (Map) questionListRequest.get(Constants.REQUEST); - if ((!ObjectUtils.isEmpty(request)) && request.containsKey(Constants.SEARCH)) { - Map searchObj = (Map) request.get(Constants.SEARCH); - if (!ObjectUtils.isEmpty(searchObj) && searchObj.containsKey(Constants.IDENTIFIER)) { - List identifierList = (List) searchObj.get(Constants.IDENTIFIER); - if (!CollectionUtils.isEmpty(identifierList)) { - return identifierList; - } - } - } - } - throw new Exception("Failed to process the questionList request body."); - } - - private Map readQuestionDetails(List identifiers) throws Exception { - try { - StringBuilder sbUrl = new StringBuilder(cbExtServerProperties.getAssessmentHost()); - sbUrl.append(cbExtServerProperties.getAssessmentQuestionListPath()); - Map headers = new HashMap<>(); - headers.put(Constants.AUTHORIZATION, cbExtServerProperties.getSbApiKey()); - Map requestBody = new HashMap(); - Map requestData = new HashMap(); - Map searchData = new HashMap(); - searchData.put(Constants.IDENTIFIER, identifiers); - requestData.put(Constants.SEARCH, searchData); - requestBody.put(Constants.REQUEST, requestData); - return outboundRequestHandlerService.fetchResultUsingPost(sbUrl.toString(), requestBody, headers); - } catch (Exception e) { - logger.error(e); - throw new Exception("Failed to process the readQuestionDetails."); - } - } - - private Map filterQuestionMapDetail(Map questionMapResponse) { - List questionParams = cbExtServerProperties.getAssessmentQuestionParams(); - Map updatedQuestionMap = new HashMap(); - for (String questionParam : questionParams) { - if (questionMapResponse.containsKey(questionParam)) { - updatedQuestionMap.put(questionParam, questionMapResponse.get(questionParam)); - } - } - if (questionMapResponse.containsKey(Constants.CHOICES) && updatedQuestionMap.containsKey(Constants.PRIMARY_CATEGORY) && !updatedQuestionMap.get(Constants.PRIMARY_CATEGORY).toString().equalsIgnoreCase(Constants.FTB_QUESTION)) { - 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, 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); - updatedQuestionMap.put(Constants.RHS_CHOICES, rhsChoicesObj); - } - - return updatedQuestionMap; - } - - private SBApiResponse prepareQuestionResponse(List updatedQuestions, boolean isSuccess) { - SBApiResponse outgoingResponse = new SBApiResponse(); - outgoingResponse.setId(Constants.API_QUESTIONS_LIST); - outgoingResponse.setVer(Constants.VER); - if (isSuccess) { - outgoingResponse.setResponseCode(HttpStatus.OK); - outgoingResponse.getResult().put(Constants.QUESTIONS, updatedQuestions); - } else { - outgoingResponse.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); - } - return outgoingResponse; - } - - private void validateSectionLevelScore(SBApiResponse outgoingResponse, Map userSectionData, - SunbirdApiResp assessmentHierarchy) { - } - - private void validateAssessmentLevelScore(SBApiResponse outgoingResponse, Map userSectionData, - Map assessmentHierarchy) { - // First Get the Hierarchy of given AssessmentId - List> hierarchySectionList = (List>) assessmentHierarchy - .get(Constants.CHILDREN); - if (CollectionUtils.isEmpty(hierarchySectionList)) { - logger.error(new Exception("There are no section details in Assessment hierarchy.")); - // TODO Throw error - return; - } - String userSectionId = (String) userSectionData.get(Constants.IDENTIFIER); - - Map hierarchySection = null; - for (Map section : hierarchySectionList) { - String hierarchySectionId = (String) section.get(Constants.IDENTIFIER); - if (userSectionId.equalsIgnoreCase(hierarchySectionId)) { - hierarchySection = section; - break; - } - } - - if (ObjectUtils.isEmpty(hierarchySection)) { - // TODO - throw error - return; - } - - // We have both hierarchySection and userSection - // Get the list of question Identifier's from userSectionData - List questionIdList = new ArrayList(); - List> userQuestionList = (List>) hierarchySection.get(Constants.CHILDREN); - for (Map question : userQuestionList) { - questionIdList.add((String) question.get(Constants.IDENTIFIER)); - } - - // We have both answer and user given data. This needs to be compared and result - // should be return. - Map resultMap = assessUtilServ.validateQumlAssessment(questionIdList, - (List>) userSectionData.get(Constants.CHILDREN)); - - Double result = (Double) resultMap.get(Constants.RESULT); - int passPercentage = (Integer) hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE); - Map sectionLevelResult = new HashMap(); - sectionLevelResult.put(Constants.IDENTIFIER, hierarchySection.get(Constants.IDENTIFIER)); - sectionLevelResult.put(Constants.OBJECT_TYPE, hierarchySection.get(Constants.OBJECT_TYPE)); - sectionLevelResult.put(Constants.PRIMARY_CATEGORY, hierarchySection.get(Constants.PRIMARY_CATEGORY)); - sectionLevelResult.put(Constants.SCORE_CUTOFF_TYPE, hierarchySection.get(Constants.SCORE_CUTOFF_TYPE)); - sectionLevelResult.put(Constants.PASS_PERCENTAGE, passPercentage); - sectionLevelResult.put(Constants.RESULT, result); - sectionLevelResult.put(Constants.TOTAL, resultMap.get(Constants.TOTAL)); - sectionLevelResult.put(Constants.BLANK, resultMap.get(Constants.BLANK)); - sectionLevelResult.put(Constants.CORRECT, resultMap.get(Constants.CORRECT)); - sectionLevelResult.put(Constants.PASS_PERCENTAGE, hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE)); - sectionLevelResult.put(Constants.INCORRECT, resultMap.get(Constants.INCORRECT)); - sectionLevelResult.put(Constants.PASS, result >= passPercentage); - - List> sectionChildren = new ArrayList>(); - sectionChildren.add(sectionLevelResult); - outgoingResponse.getResult().put(Constants.CHILDREN, sectionChildren); - - outgoingResponse.getResult().put(Constants.OVERALL_RESULT, result); - outgoingResponse.getResult().put(Constants.TOTAL, resultMap.get(Constants.TOTAL)); - outgoingResponse.getResult().put(Constants.BLANK, resultMap.get(Constants.BLANK)); - outgoingResponse.getResult().put(Constants.CORRECT, resultMap.get(Constants.CORRECT)); - outgoingResponse.getResult().put(Constants.PASS_PERCENTAGE, hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE)); - outgoingResponse.getResult().put(Constants.INCORRECT, resultMap.get(Constants.INCORRECT)); - outgoingResponse.getResult().put(Constants.PASS, result >= passPercentage); - } - -} + + private final Logger logger = LoggerFactory.getLogger(AssessmentServiceV2Impl.class); + + @Autowired + AssessmentUtilServiceV2 assessUtilServ; + + @Autowired + CbExtServerProperties serverProperties; + + @Autowired + Producer kafkaProducer; + + @Autowired + AssessmentRepository assessmentRepository; + + @Autowired + RequestInterceptor requestInterceptor; + + public SBApiResponse readAssessment(String assessmentIdentifier, String token) { + logger.info("AssessmentServiceV2Impl::readAssessment... Started"); + SBApiResponse response = createDefaultResponse(Constants.API_QUESTIONSET_HIERARCHY_GET); + String errMsg; + try { + String userId = validateAuthTokenAndFetchUserId(token); + if (userId != null) { + logger.info("readAssessment.. userId :" + userId); + Map assessmentAllDetail = new HashMap<>(); + errMsg = fetchReadHierarchyDetails(assessmentAllDetail, token, assessmentIdentifier); + if (errMsg.isEmpty() && !((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + logger.info("Fetched assessment Details... for : " + assessmentIdentifier); + List> existingDataList = assessmentRepository + .fetchUserAssessmentDataFromDB(userId, assessmentIdentifier); + Timestamp assessmentStartTime = new Timestamp(new Date().getTime()); + if (existingDataList.isEmpty()) { + logger.info("Assessment read first time for user."); + response.getResult().put(Constants.QUESTION_SET, readAssessmentLevelData(assessmentAllDetail)); + int expectedDuration = (Integer) assessmentAllDetail.get(Constants.EXPECTED_DURATION); + Boolean isAssessmentUpdatedToDB = assessmentRepository.addUserAssesmentDataToDB(userId, + assessmentIdentifier, assessmentStartTime, + calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime), + (Map) (response.getResult().get(Constants.QUESTION_SET)), + Constants.NOT_SUBMITTED); + if (Boolean.FALSE.equals(isAssessmentUpdatedToDB)) { + errMsg = Constants.ASSESSMENT_DATA_START_TIME_NOT_UPDATED; + } + } else { + logger.info("Assessment read... user has details... "); + Date existingAssessmentEndTime = (Date) (existingDataList.get(0).get(Constants.END_TIME)); + int time = assessmentStartTime.compareTo(existingAssessmentEndTime); + if (time < 0 && ((String) existingDataList.get(0).get(Constants.STATUS)) + .equalsIgnoreCase(Constants.NOT_SUBMITTED)) { + String questionSetFromAssessmentString = (String) existingDataList.get(0) + .get(Constants.ASSESSMENT_READ_RESPONSE); + Map questionSetFromAssessment = new Gson().fromJson( + questionSetFromAssessmentString, new TypeToken>() { + }.getType()); + response.getResult().put(Constants.QUESTION_SET, questionSetFromAssessment); + } else { + logger.info("Assessment read... adding user data to db..."); + response.getResult().put(Constants.QUESTION_SET, + readAssessmentLevelData(assessmentAllDetail)); + int expectedDuration = (Integer) assessmentAllDetail.get(Constants.EXPECTED_DURATION); + Boolean isAssessmentUpdatedToDB = assessmentRepository.addUserAssesmentDataToDB(userId, + assessmentIdentifier, assessmentStartTime, + calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime), + (Map) (response.getResult().get(Constants.QUESTION_SET)), + Constants.NOT_SUBMITTED); + if (Boolean.FALSE.equals(isAssessmentUpdatedToDB)) { + errMsg = Constants.ASSESSMENT_DATA_START_TIME_NOT_UPDATED; + } + } + } + } else if (errMsg.isEmpty() && ((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + response.getResult().put(Constants.QUESTION_SET, readAssessmentLevelData(assessmentAllDetail)); + } + } else { + errMsg = Constants.USER_ID_DOESNT_EXIST; + } + } catch (Exception e) { + logger.error(String.format("Exception in %s : %s", "read Assessment", e.getMessage()), e); + errMsg = "Failed to read Assessment. Exception: " + e.getMessage(); + } + if (StringUtils.isNotBlank(errMsg)) { + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(errMsg); + response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); + } + return response; + } + + public SBApiResponse readQuestionList(Map requestBody, String authUserToken) { + SBApiResponse response = createDefaultResponse(Constants.API_SUBMIT_ASSESSMENT); + String errMsg; + try { + List identifierList = new ArrayList<>(); + List questionList = new ArrayList<>(); + errMsg = validateQuestionListAPI(requestBody, authUserToken, identifierList); + if (errMsg.isEmpty()) { + errMsg = assessUtilServ.fetchQuestionIdentifierValue(identifierList, questionList); + if (errMsg.isEmpty() && identifierList.size() == questionList.size()) { + response.getResult().put(Constants.QUESTIONS, questionList); + } + } + } catch (Exception e) { + logger.error(String.format("Exception in %s : %s", "get Question List", e.getMessage()), e); + errMsg = "Failed to fetch the question list. Exception: " + e.getMessage(); + } + if (StringUtils.isNotBlank(errMsg)) { + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(errMsg); + response.setResponseCode(HttpStatus.BAD_REQUEST); + } + return response; + + } + + private String validateAuthTokenAndFetchUserId(String authUserToken) { + return requestInterceptor.fetchUserIdFromAccessToken(authUserToken); + } + + private String fetchReadHierarchyDetails(Map assessmentAllDetail, String token, + String assessmentIdentifier) { + Map readHierarchyApiResponse = assessUtilServ.getReadHierarchyApiResponse(assessmentIdentifier, + token); + if (readHierarchyApiResponse.isEmpty() + || !Constants.OK.equalsIgnoreCase((String) readHierarchyApiResponse.get(Constants.RESPONSE_CODE))) { + return Constants.ASSESSMENT_HIERARCHY_READ_FAILED; + } + assessmentAllDetail + .putAll((Map) ((Map) readHierarchyApiResponse.get(Constants.RESULT)) + .get(Constants.QUESTION_SET)); + + return StringUtils.EMPTY; + } + + private String validateQuestionListAPI(Map requestBody, String authUserToken, + List identifierList) { + String userId = validateAuthTokenAndFetchUserId(authUserToken); + if (StringUtils.isBlank(userId)) { + return Constants.USER_ID_DOESNT_EXIST; + } + + if (StringUtils.isBlank((String) requestBody.get(Constants.ASSESSMENT_ID_KEY))) { + return Constants.ASSESSMENT_ID_KEY_IS_NOT_PRESENT_IS_EMPTY; + } + + identifierList.addAll(getQuestionIdList(requestBody)); + if (identifierList.isEmpty()) { + return Constants.IDENTIFIER_LIST_IS_EMPTY; + } + + Map assessmentDetail = new HashMap<>(); + fetchReadHierarchyDetails(assessmentDetail, authUserToken, + (String) requestBody.get(Constants.ASSESSMENT_ID_KEY)); + + if (ObjectUtils.isEmpty(assessmentDetail)) { + return Constants.ASSESSMENT_HIERARCHY_READ_FAILED; + } + + if (!((String) assessmentDetail.get(Constants.PRIMARY_CATEGORY)) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + (String) requestBody.get(Constants.ASSESSMENT_ID_KEY)); + String questionSetFromAssessmentString = (!existingDataList.isEmpty()) + ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) + : ""; + if (!questionSetFromAssessmentString.isEmpty()) { + Map questionSetFromAssessment = new Gson().fromJson(questionSetFromAssessmentString, + new TypeToken>() { + }.getType()); + List questionsFromAssessment = new ArrayList<>(); + List> sections = (List>) questionSetFromAssessment + .get(Constants.CHILDREN); + for (Map section : sections) { + questionsFromAssessment.addAll((List) section.get(Constants.CHILD_NODES)); + } + // Out of the list of questions received in the payload, checking if the request + // has only those ids which are a part of the user's latest assessment + // Fetching all the remaining questions details from the Redis + if (Boolean.FALSE.equals(validateQuestionListRequest(identifierList, questionsFromAssessment))) { + return Constants.THE_QUESTIONS_IDS_PROVIDED_DONT_MATCH; + } + } else { + return Constants.ASSESSMENT_ID_INVALID_SESSION_EXPIRED; + } + } + return ""; + } + + @Override + public SBApiResponse submitAssessment(Map submitRequest, String authUserToken) { + SBApiResponse outgoingResponse = createDefaultResponse(Constants.API_SUBMIT_ASSESSMENT); + String errMsg; + List> sectionListFromSubmitRequest = new ArrayList<>(); + List> hierarchySectionList = new ArrayList<>(); + Map allHierarchy = new HashMap<>(); + List questionsListFromAssessmentHierarchy = new ArrayList<>(); + errMsg = validateSubmitAssessmentRequest(submitRequest, authUserToken, hierarchySectionList, + sectionListFromSubmitRequest, allHierarchy); + if (errMsg.isEmpty()) { + String userId = validateAuthTokenAndFetchUserId(authUserToken); + String scoreCutOffType = ((String) allHierarchy.get(Constants.SCORE_CUTOFF_TYPE)).toLowerCase(); + List> existingDataList = new ArrayList<>(); + List> sectionLevelsResults = new ArrayList<>(); + for (Map hierarchySection : hierarchySectionList) { + String hierarchySectionId = (String) hierarchySection.get(Constants.IDENTIFIER); + String userSectionId = ""; + Map userSectionData = new HashMap<>(); + for (Map sectionFromSubmitRequest : sectionListFromSubmitRequest) { + userSectionId = (String) sectionFromSubmitRequest.get(Constants.IDENTIFIER); + if (userSectionId.equalsIgnoreCase(hierarchySectionId)) { + userSectionData = sectionFromSubmitRequest; + break; + } + } + if (!((String) (allHierarchy.get(Constants.PRIMARY_CATEGORY))) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + (String) submitRequest.get(Constants.IDENTIFIER)); + String questionSetFromAssessmentString = (!existingDataList.isEmpty()) + ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) + : ""; + if (!questionSetFromAssessmentString.isEmpty()) { + Map questionSetFromAssessment = new Gson() + .fromJson(questionSetFromAssessmentString, new TypeToken>() { + }.getType()); + if (questionSetFromAssessment != null + && questionSetFromAssessment.get(Constants.CHILDREN) != null) { + List> sections = (List>) questionSetFromAssessment + .get(Constants.CHILDREN); + for (Map section : sections) { + String sectionId = (String) section.get(Constants.IDENTIFIER); + if (userSectionId.equalsIgnoreCase(sectionId)) { + questionsListFromAssessmentHierarchy = (List) section + .get(Constants.CHILD_NODES); + break; + } + } + } else { + errMsg = "Question Set From The Database returns Null"; + outgoingResponse.getResult().clear(); + break; + } + + hierarchySection.put(Constants.SCORE_CUTOFF_TYPE, scoreCutOffType); + List> questionsListFromSubmitRequest = new ArrayList<>(); + if (userSectionData.containsKey(Constants.CHILDREN) + && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { + questionsListFromSubmitRequest = (List>) userSectionData + .get(Constants.CHILDREN); + } + Map result = new HashMap<>(); + switch (scoreCutOffType) { + case Constants.ASSESSMENT_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + outgoingResponse.getResult().putAll(calculateAssessmentFinalResults(result)); + writeDataToDatabaseAndTriggerKafkaEvent(submitRequest, userId, existingDataList, result, + (String) allHierarchy.get(Constants.PRIMARY_CATEGORY)); + return outgoingResponse; + } + case Constants.SECTION_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + sectionLevelsResults.add(result); + } + break; + default: + break; + } + } + } else { + hierarchySection.put(Constants.SCORE_CUTOFF_TYPE, scoreCutOffType); + List> questionsListFromSubmitRequest = new ArrayList<>(); + if (userSectionData.containsKey(Constants.CHILDREN) + && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { + questionsListFromSubmitRequest = (List>) userSectionData + .get(Constants.CHILDREN); + } + List desiredKeys = Lists.newArrayList(Constants.IDENTIFIER); + List questionsList = questionsListFromSubmitRequest.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); + questionsListFromAssessmentHierarchy = questionsList.stream() + .map(object -> Objects.toString(object, null)).collect(Collectors.toList()); + Map result = new HashMap<>(); + switch (scoreCutOffType) { + case Constants.ASSESSMENT_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + outgoingResponse.getResult().putAll(calculateAssessmentFinalResults(result)); + return outgoingResponse; + } + case Constants.SECTION_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + sectionLevelsResults.add(result); + } + break; + default: + break; + } + } + } + if (errMsg.isEmpty() && !ObjectUtils.isEmpty(scoreCutOffType) + && scoreCutOffType.equalsIgnoreCase(Constants.SECTION_LEVEL_SCORE_CUTOFF)) { + Map result = calculateSectionFinalResults(sectionLevelsResults); + outgoingResponse.getResult().putAll(result); + writeDataToDatabaseAndTriggerKafkaEvent(submitRequest, userId, existingDataList, result, + (String) allHierarchy.get(Constants.PRIMARY_CATEGORY)); + return outgoingResponse; + } + } + if (StringUtils.isNotBlank(errMsg)) { + outgoingResponse.getParams().setStatus(Constants.FAILED); + outgoingResponse.getParams().setErrmsg(errMsg); + outgoingResponse.setResponseCode(HttpStatus.BAD_REQUEST); + } + return outgoingResponse; + } + + private void writeDataToDatabaseAndTriggerKafkaEvent(Map submitRequest, String userId, + List> existingDataList, Map result, String primaryCategory) { + Date startTime = (!existingDataList.isEmpty()) ? (Date) existingDataList.get(0).get(Constants.START_TIME) + : null; + Boolean isAssessmentUpdatedToDB = assessmentRepository.updateUserAssesmentDataToDB(userId, + (String) submitRequest.get(Constants.IDENTIFIER), submitRequest, result, Constants.SUBMITTED, + startTime); + if (Boolean.TRUE.equals(isAssessmentUpdatedToDB)) { + Map kafkaResult = new HashMap<>(); + kafkaResult.put(Constants.CONTENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); + kafkaResult.put(Constants.COURSE_ID, submitRequest.get(Constants.COURSE_ID)); + kafkaResult.put(Constants.BATCH_ID, submitRequest.get(Constants.BATCH_ID)); + kafkaResult.put(Constants.USER_ID, submitRequest.get(Constants.USER_ID)); + kafkaResult.put(Constants.ASSESSMENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); + kafkaResult.put(Constants.PRIMARY_CATEGORY, primaryCategory); + kafkaProducer.push(serverProperties.getAssessmentSubmitTopic(), kafkaResult); + } + } + + private String validateSubmitAssessmentRequest(Map submitRequest, String authUserToken, + List> hierarchySectionList, List> sectionListFromSubmitRequest, + Map assessmentHierarchy) { + String userId = validateAuthTokenAndFetchUserId(authUserToken); + if (ObjectUtils.isEmpty(userId)) { + return Constants.USER_ID_DOESNT_EXIST; + } + submitRequest.put(Constants.USER_ID, userId); + if (StringUtils.isEmpty((String) submitRequest.get(Constants.IDENTIFIER))) { + return Constants.INVALID_ASSESSMENT_ID; + } + String assessmentIdFromRequest = (String) submitRequest.get(Constants.IDENTIFIER); + String errMsg = fetchReadHierarchyDetails(assessmentHierarchy, authUserToken, assessmentIdFromRequest); + if (!errMsg.isEmpty()) { + return errMsg; + } + if (ObjectUtils.isEmpty(assessmentHierarchy)) { + return Constants.READ_ASSESSMENT_FAILED; + } + hierarchySectionList.addAll((List>) assessmentHierarchy.get(Constants.CHILDREN)); + sectionListFromSubmitRequest.addAll((List>) submitRequest.get(Constants.CHILDREN)); + if (((String) (assessmentHierarchy.get(Constants.PRIMARY_CATEGORY))) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) + return ""; + List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + assessmentIdFromRequest); + Date assessmentStartTime = (!existingDataList.isEmpty()) + ? (Date) existingDataList.get(0).get(Constants.START_TIME) + : null; + if (assessmentStartTime == null) { + return Constants.READ_ASSESSMENT_START_TIME_FAILED; + } + int expectedDuration = (Integer) assessmentHierarchy.get(Constants.EXPECTED_DURATION); + Timestamp later = calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime); + Timestamp submissionTime = new Timestamp(new Date().getTime()); + int time = submissionTime.compareTo(later); + if (time <= 0) { + List desiredKeys = Lists.newArrayList(Constants.IDENTIFIER); + List hierarchySectionIds = hierarchySectionList.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); + List submitSectionIds = sectionListFromSubmitRequest.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); + if (!new HashSet<>(hierarchySectionIds).containsAll(submitSectionIds)) { + return Constants.WRONG_SECTION_DETAILS; + } else { + String areQuestionIdsSame = validateIfQuestionIdsAreSame(submitRequest, sectionListFromSubmitRequest, + desiredKeys, userId); + if (!areQuestionIdsSame.isEmpty()) + return areQuestionIdsSame; + } + } else { + return Constants.ASSESSMENT_SUBMIT_EXPIRED; + } + return ""; + } + + private String validateIfQuestionIdsAreSame(Map submitRequest, + List> sectionListFromSubmitRequest, List desiredKeys, String userId) { + List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + (String) submitRequest.get(Constants.IDENTIFIER)); + String questionSetFromAssessmentString = (!existingDataList.isEmpty()) + ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) + : ""; + if (!questionSetFromAssessmentString.isEmpty()) { + Map questionSetFromAssessment = new Gson().fromJson(questionSetFromAssessmentString, + new TypeToken>() { + }.getType()); + if (questionSetFromAssessment != null && questionSetFromAssessment.get(Constants.CHILDREN) != null) { + List> sections = (List>) questionSetFromAssessment + .get(Constants.CHILDREN); + List desiredKey = Lists.newArrayList(Constants.CHILD_NODES); + List questionList = sections.stream() + .flatMap(x -> desiredKey.stream().filter(x::containsKey).map(x::get)).collect(toList()); + List questionIdsFromAssessmentHierarchy = new ArrayList<>(); + List> questionsListFromSubmitRequest = new ArrayList<>(); + for (Object question : questionList) { + questionIdsFromAssessmentHierarchy.addAll((List) question); + } + for (Map userSectionData : sectionListFromSubmitRequest) { + if (userSectionData.containsKey(Constants.CHILDREN) + && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { + questionsListFromSubmitRequest + .addAll((List>) userSectionData.get(Constants.CHILDREN)); + } + } + List userQuestionIdsFromSubmitRequest = questionsListFromSubmitRequest.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)) + .collect(Collectors.toList()); + if (!new HashSet<>(questionIdsFromAssessmentHierarchy).containsAll(userQuestionIdsFromSubmitRequest)) { + return Constants.ASSESSMENT_SUBMIT_INVALID_QUESTION; + } + } + } else { + return Constants.ASSESSMENT_SUBMIT_QUESTION_READ_FAILED; + } + return ""; + } + + private Timestamp calculateAssessmentSubmitTime(int expectedDuration, Date assessmentStartTime) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(new Timestamp(assessmentStartTime.getTime()).getTime()); + if (serverProperties.getUserAssessmentSubmissionDuration().isEmpty()) { + serverProperties.setUserAssessmentSubmissionDuration("120"); + } + cal.add(Calendar.SECOND, + expectedDuration + Integer.parseInt(serverProperties.getUserAssessmentSubmissionDuration())); + return new Timestamp(cal.getTime().getTime()); + } + + private Map calculateAssessmentFinalResults(Map assessmentLevelResult) { + Map res = new HashMap<>(); + try { + res.put(Constants.CHILDREN, Collections.singletonList(assessmentLevelResult)); + Double result = (Double) assessmentLevelResult.get(Constants.RESULT); + res.put(Constants.OVERALL_RESULT, result); + res.put(Constants.TOTAL, assessmentLevelResult.get(Constants.TOTAL)); + res.put(Constants.BLANK, assessmentLevelResult.get(Constants.BLANK)); + res.put(Constants.CORRECT, assessmentLevelResult.get(Constants.CORRECT)); + res.put(Constants.PASS_PERCENTAGE, assessmentLevelResult.get(Constants.PASS_PERCENTAGE)); + res.put(Constants.INCORRECT, assessmentLevelResult.get(Constants.INCORRECT)); + Integer minimumPassPercentage = (Integer) assessmentLevelResult.get(Constants.PASS_PERCENTAGE); + res.put(Constants.PASS, result >= minimumPassPercentage); + } catch (Exception e) { + logger.info(e.getMessage()); + } + return res; + } + + private Map calculateSectionFinalResults(List> sectionLevelResults) { + Map res = new HashMap<>(); + Double result; + Integer correct = 0; + Integer blank = 0; + Integer inCorrect = 0; + Integer total = 0; + int pass = 0; + Double totalResult = 0.0; + try { + for (Map sectionChildren : sectionLevelResults) { + res.put(Constants.CHILDREN, sectionLevelResults); + result = (Double) sectionChildren.get(Constants.RESULT); + totalResult += result; + total += (Integer) sectionChildren.get(Constants.TOTAL); + blank += (Integer) sectionChildren.get(Constants.BLANK); + correct += (Integer) sectionChildren.get(Constants.CORRECT); + inCorrect += (Integer) sectionChildren.get(Constants.INCORRECT); + Integer minimumPassPercentage = (Integer) sectionChildren.get(Constants.PASS_PERCENTAGE); + if (result >= minimumPassPercentage) { + pass++; + } + } + res.put(Constants.OVERALL_RESULT, totalResult / sectionLevelResults.size()); + res.put(Constants.TOTAL, total); + res.put(Constants.BLANK, blank); + res.put(Constants.CORRECT, correct); + res.put(Constants.INCORRECT, inCorrect); + res.put(Constants.PASS, (pass == sectionLevelResults.size())); + } catch (Exception e) { + logger.info(e.getMessage()); + } + return res; + } + + private Map readAssessmentLevelData(Map assessmentAllDetail) { + List assessmentParams = serverProperties.getAssessmentLevelParams(); + Map assessmentFilteredDetail = new HashMap<>(); + for (String assessmentParam : assessmentParams) { + if ((assessmentAllDetail.containsKey(assessmentParam))) { + assessmentFilteredDetail.put(assessmentParam, assessmentAllDetail.get(assessmentParam)); + } + } + readSectionLevelParams(assessmentAllDetail, assessmentFilteredDetail); + return assessmentFilteredDetail; + } + + private void readSectionLevelParams(Map assessmentAllDetail, + Map assessmentFilteredDetail) { + List> sectionResponse = new ArrayList<>(); + List sectionIdList = new ArrayList<>(); + List sectionParams = serverProperties.getAssessmentSectionParams(); + List> sections = (List>) assessmentAllDetail.get(Constants.CHILDREN); + for (Map section : sections) { + sectionIdList.add((String) section.get(Constants.IDENTIFIER)); + Map newSection = new HashMap<>(); + for (String sectionParam : sectionParams) { + if (section.containsKey(sectionParam)) { + newSection.put(sectionParam, section.get(sectionParam)); + } + } + List allQuestionIdList = new ArrayList<>(); + List> questions = (List>) section.get(Constants.CHILDREN); + for (Map question : questions) { + allQuestionIdList.add((String) question.get(Constants.IDENTIFIER)); + } + Collections.shuffle(allQuestionIdList); + List childNodeList = new ArrayList<>(); + if (!ObjectUtils.isEmpty(section.get(Constants.MAX_QUESTIONS))) { + int maxQuestions = (int) section.get(Constants.MAX_QUESTIONS); + childNodeList = allQuestionIdList.stream().limit(maxQuestions).collect(toList()); + } + newSection.put(Constants.CHILD_NODES, childNodeList); + sectionResponse.add(newSection); + } + assessmentFilteredDetail.put(Constants.CHILDREN, sectionResponse); + assessmentFilteredDetail.put(Constants.CHILD_NODES, sectionIdList); + } + + private List getQuestionIdList(Map questionListRequest) { + try { + if (questionListRequest.containsKey(Constants.REQUEST)) { + Map request = (Map) questionListRequest.get(Constants.REQUEST); + if ((!ObjectUtils.isEmpty(request)) && request.containsKey(Constants.SEARCH)) { + Map searchObj = (Map) request.get(Constants.SEARCH); + if (!ObjectUtils.isEmpty(searchObj) && searchObj.containsKey(Constants.IDENTIFIER) + && !CollectionUtils.isEmpty((List) searchObj.get(Constants.IDENTIFIER))) { + return (List) searchObj.get(Constants.IDENTIFIER); + } + } + } + } catch (Exception e) { + logger.error(String.format("Failed to process the questionList request body. %s", e.getMessage())); + } + return Collections.emptyList(); + } + + public Map createResponseMapWithProperStructure(Map hierarchySection, + Map resultMap) { + Map sectionLevelResult = new HashMap<>(); + sectionLevelResult.put(Constants.IDENTIFIER, hierarchySection.get(Constants.IDENTIFIER)); + sectionLevelResult.put(Constants.OBJECT_TYPE, hierarchySection.get(Constants.OBJECT_TYPE)); + sectionLevelResult.put(Constants.PRIMARY_CATEGORY, hierarchySection.get(Constants.PRIMARY_CATEGORY)); + sectionLevelResult.put(Constants.PASS_PERCENTAGE, hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE)); + Double result; + if (!ObjectUtils.isEmpty(resultMap)) { + result = (Double) resultMap.get(Constants.RESULT); + sectionLevelResult.put(Constants.RESULT, result); + sectionLevelResult.put(Constants.TOTAL, resultMap.get(Constants.TOTAL)); + sectionLevelResult.put(Constants.BLANK, resultMap.get(Constants.BLANK)); + sectionLevelResult.put(Constants.CORRECT, resultMap.get(Constants.CORRECT)); + sectionLevelResult.put(Constants.INCORRECT, resultMap.get(Constants.INCORRECT)); + } else { + result = 0.0; + sectionLevelResult.put(Constants.RESULT, result); + List childNodes = (List) hierarchySection.get(Constants.CHILDREN); + sectionLevelResult.put(Constants.TOTAL, childNodes.size()); + sectionLevelResult.put(Constants.BLANK, childNodes.size()); + sectionLevelResult.put(Constants.CORRECT, 0); + sectionLevelResult.put(Constants.INCORRECT, 0); + } + sectionLevelResult.put(Constants.PASS, + result >= ((Integer) hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE))); + sectionLevelResult.put(Constants.OVERALL_RESULT, result); + return sectionLevelResult; + } + + private SBApiResponse createDefaultResponse(String api) { + SBApiResponse response = new SBApiResponse(); + response.setId(api); + response.setVer(Constants.VER); + response.getParams().setResmsgid(UUID.randomUUID().toString()); + response.getParams().setStatus(Constants.SUCCESS); + response.setResponseCode(HttpStatus.OK); + response.setTs(DateTime.now().toString()); + return response; + } + + private Boolean validateQuestionListRequest(List identifierList, List questionsFromAssessment) { + return (new HashSet<>(questionsFromAssessment).containsAll(identifierList)) ? Boolean.TRUE : Boolean.FALSE; + } +} \ 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 dc3f02514..a786eccd1 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java @@ -6,4 +6,8 @@ public interface AssessmentUtilServiceV2 { public Map validateQumlAssessment(List originalQuestionList, List> userQuestionList); + + public String fetchQuestionIdentifierValue(List identifierList, List questionList) throws Exception; + + public Map getReadHierarchyApiResponse(String assessmentIdentifier, String token); } diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java index e7384b23b..3f6557a8d 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java @@ -6,39 +6,28 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; -import org.sunbird.cache.RedisCacheMgr; +import org.sunbird.common.service.OutboundRequestHandlerServiceImpl; +import org.sunbird.common.util.CbExtServerProperties; import org.sunbird.common.util.Constants; -import org.sunbird.core.exception.ApplicationLogicError; -import org.sunbird.core.logger.CbExtLogger; + +import com.fasterxml.jackson.databind.ObjectMapper; @Service public class AssessmentUtilServiceV2Impl implements AssessmentUtilServiceV2 { @Autowired - RedisCacheMgr redisCacheMgr; - - private CbExtLogger logger = new CbExtLogger(getClass().getName()); - - public static final String QUESTION_TYPE = "qType"; - public static final String OPTIONS = "options"; - public static final String IS_CORRECT = "isCorrect"; - public static final String OPTION_ID = "optionId"; - public static final String MCQ_SCA = "mcq-sca"; - public static final String MCQ_MCA = "mcq-mca"; - public static final String FTB = "ftb"; - public static final String MTF = "mtf"; - public static final String QUESTION_ID = "questionId"; - public static final String RESPONSE = "response"; - public static final String ANSWER = "answer"; - public static final String VALUE = "value"; - public static final String EDITOR_STATE = "editorState"; - public static final String BODY = "body"; - public static final String SELECTED_ANSWER = "selectedAnswer"; - public static final String INDEX = "index"; + CbExtServerProperties serverProperties; + + @Autowired + OutboundRequestHandlerServiceImpl outboundRequestHandlerService; + + private Logger logger = LoggerFactory.getLogger(AssessmentUtilServiceV2Impl.class); public Map validateQumlAssessment(List originalQuestionList, List> userQuestionList) { @@ -52,38 +41,35 @@ public Map validateQumlAssessment(List originalQuestionL Map answers = getQumlAnswers(originalQuestionList); for (Map question : userQuestionList) { List marked = new ArrayList<>(); - if (question.containsKey(QUESTION_TYPE)) { - String questionType = ((String) question.get(QUESTION_TYPE)).toLowerCase(); - Map editorStateObj = (Map) question.get(EDITOR_STATE); - List> options = (List>) editorStateObj.get(OPTIONS); + if (question.containsKey(Constants.QUESTION_TYPE)) { + String questionType = ((String) question.get(Constants.QUESTION_TYPE)).toLowerCase(); + Map editorStateObj = (Map) question.get(Constants.EDITOR_STATE); + List> options = (List>) editorStateObj + .get(Constants.OPTIONS); switch (questionType) { - case MTF: + case Constants.MTF: for (Map option : options) { - marked.add(option.get(INDEX).toString() + "-" - + option.get(SELECTED_ANSWER).toString().toLowerCase()); + marked.add(option.get(Constants.INDEX).toString() + "-" + + option.get(Constants.SELECTED_ANSWER).toString().toLowerCase()); } break; - case FTB: + case Constants.FTB: for (Map option : options) { - marked.add((String) option.get(SELECTED_ANSWER)); + marked.add((String) option.get(Constants.SELECTED_ANSWER)); } break; - case MCQ_SCA: - case MCQ_MCA: + case Constants.MCQ_SCA: + case Constants.MCQ_MCA: for (Map option : options) { - if ((boolean) option.get(SELECTED_ANSWER)) { - marked.add((String) option.get(INDEX)); + if ((boolean) option.get(Constants.SELECTED_ANSWER)) { + marked.add((String) option.get(Constants.INDEX)); } } break; default: break; } - } else { - // TODO - how to handle this case?? - // Currently throw error } - if (CollectionUtils.isEmpty(marked)) blank++; else { @@ -97,60 +83,66 @@ public Map validateQumlAssessment(List originalQuestionL else inCorrect++; } - total++; } // Increment the blank counter for skipped question objects if (answers.size() > userQuestionList.size()) { blank += answers.size() - userQuestionList.size(); } - result = ((correct * 100d) / (correct + blank + inCorrect)); - resultMap.put("result", result); - resultMap.put("incorrect", inCorrect); - resultMap.put("blank", blank); - resultMap.put("correct", correct); - resultMap.put("total", total); + total = correct + blank + inCorrect; + resultMap.put(Constants.RESULT, ((correct * 100d) / total)); + resultMap.put(Constants.INCORRECT, inCorrect); + resultMap.put(Constants.BLANK, blank); + resultMap.put(Constants.CORRECT, correct); + resultMap.put(Constants.TOTAL, total); return resultMap; } catch (Exception ex) { - logger.error(ex); - throw new ApplicationLogicError("Error when verifying assessment. Error : " + ex.getMessage(), ex); + logger.error("Error when verifying assessment. Error : "); } + return new HashMap<>(); } private Map getQumlAnswers(List questions) throws Exception { Map ret = new HashMap<>(); + + Map> questionMap = new HashMap>(); + fetchQuestionMapDetails(questions, questionMap); + for (String questionId : questions) { List correctOption = new ArrayList<>(); - Map question = (Map) redisCacheMgr - .getCache(Constants.QUESTION_ID + questionId); + Map question = questionMap.get(questionId); if (ObjectUtils.isEmpty(question)) { - logger.error(new Exception("Failed to get the answer for question: " + questionId)); - // TODO - Need to handle this scenario. + logger.error("Failed to get the answer for question: " + questionId); + // call the assessment question list api + continue; } - if (question.containsKey(QUESTION_TYPE)) { - String questionType = ((String) question.get(QUESTION_TYPE)).toLowerCase(); - Map editorStateObj = (Map) question.get(EDITOR_STATE); - List> options = (List>) editorStateObj.get(OPTIONS); + if (question.containsKey(Constants.QUESTION_TYPE)) { + String questionType = ((String) question.get(Constants.QUESTION_TYPE)).toLowerCase(); + Map editorStateObj = (Map) question.get(Constants.EDITOR_STATE); + List> options = (List>) editorStateObj.get(Constants.OPTIONS); switch (questionType) { - case MTF: + case Constants.MTF: for (Map option : options) { - Map valueObj = (Map) option.get(VALUE); - correctOption.add( - valueObj.get(VALUE).toString() + "-" + option.get(ANSWER).toString().toLowerCase()); + Map valueObj = (Map) option.get(Constants.VALUE); + correctOption.add(valueObj.get(Constants.VALUE).toString() + "-" + + option.get(Constants.ANSWER).toString().toLowerCase()); } break; - case FTB: + case Constants.FTB: for (Map option : options) { - correctOption.add((String) option.get(SELECTED_ANSWER)); + if ((boolean) option.get(Constants.ANSWER)) { + Map valueObj = (Map) option.get(Constants.VALUE); + correctOption.add(valueObj.get(Constants.BODY).toString()); + } } break; - case MCQ_SCA: - case MCQ_MCA: + case Constants.MCQ_SCA: + case Constants.MCQ_MCA: for (Map option : options) { - if ((boolean) option.get(ANSWER)) { - Map valueObj = (Map) option.get(VALUE); - correctOption.add(valueObj.get(VALUE).toString()); + if ((boolean) option.get(Constants.ANSWER)) { + Map valueObj = (Map) option.get(Constants.VALUE); + correctOption.add(valueObj.get(Constants.VALUE).toString()); } } break; @@ -158,9 +150,9 @@ private Map getQumlAnswers(List questions) throws Except break; } } else { - for (Map options : (List>) question.get(OPTIONS)) { - if ((boolean) options.get(IS_CORRECT)) - correctOption.add(options.get(OPTION_ID).toString()); + for (Map options : (List>) question.get(Constants.OPTIONS)) { + if ((boolean) options.get(Constants.IS_CORRECT)) + correctOption.add(options.get(Constants.OPTION_ID).toString()); } } ret.put(question.get(Constants.IDENTIFIER).toString(), correctOption); @@ -168,4 +160,143 @@ private Map getQumlAnswers(List questions) throws Except return ret; } + + private void fetchQuestionMapDetails(List questions, Map> questionsMap) { + List newIdentifierList = new ArrayList<>(); + newIdentifierList.addAll(questions); + + // Taking the list which was formed with the not found values in Redis, we are + // making an internal POST call to Question List API to fetch the details + if (!newIdentifierList.isEmpty()) { + List> questionMapList = readQuestionDetails(newIdentifierList); + for (Map questionMapResponse : questionMapList) { + if (!ObjectUtils.isEmpty(questionMapResponse) + && Constants.OK.equalsIgnoreCase((String) questionMapResponse.get(Constants.RESPONSE_CODE))) { + List> questionMap = ((List>) ((Map) questionMapResponse + .get(Constants.RESULT)).get(Constants.QUESTIONS)); + for (Map question : questionMap) { + if (!ObjectUtils.isEmpty(questionMap)) { + questionsMap.put((String) question.get(Constants.IDENTIFIER), question); + } + } + } + } + } + } + + @Override + public String fetchQuestionIdentifierValue(List identifierList, List questionList) + throws Exception { + List newIdentifierList = new ArrayList<>(); + newIdentifierList.addAll(identifierList); + + // Taking the list which was formed with the not found values in Redis, we are + // making an internal POST call to Question List API to fetch the details + if (!newIdentifierList.isEmpty()) { + List> questionMapList = readQuestionDetails(newIdentifierList); + for (Map questionMapResponse : questionMapList) { + if (!ObjectUtils.isEmpty(questionMapResponse) + && Constants.OK.equalsIgnoreCase((String) questionMapResponse.get(Constants.RESPONSE_CODE))) { + List> questionMap = ((List>) ((Map) questionMapResponse + .get(Constants.RESULT)).get(Constants.QUESTIONS)); + for (Map question : questionMap) { + if (!ObjectUtils.isEmpty(questionMap)) { + questionList.add(filterQuestionMapDetail(question)); + } else { + logger.error(String.format("Failed to get Question Details for Id: %s", + question.get(Constants.IDENTIFIER).toString())); + return "Failed to get Question Details for Id: %s"; + } + } + } else { + logger.error( + String.format("Failed to get Question Details from the Question List API for the IDs: %s", + newIdentifierList.toString())); + return "Failed to get Question Details from the Question List API for the IDs"; + } + } + } + return ""; + } + + private Map filterQuestionMapDetail(Map questionMapResponse) { + 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.CHOICES) + && updatedQuestionMap.containsKey(Constants.PRIMARY_CATEGORY) && !updatedQuestionMap + .get(Constants.PRIMARY_CATEGORY).toString().equalsIgnoreCase(Constants.FTB_QUESTION)) { + 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, 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); + updatedQuestionMap.put(Constants.RHS_CHOICES, rhsChoicesObj); + } + + return updatedQuestionMap; + } + + private List> readQuestionDetails(List identifiers) { + try { + StringBuilder sbUrl = new StringBuilder(serverProperties.getAssessmentHost()); + sbUrl.append(serverProperties.getAssessmentQuestionListPath()); + Map headers = new HashMap<>(); + headers.put(Constants.AUTHORIZATION, serverProperties.getSbApiKey()); + Map requestBody = new HashMap<>(); + Map requestData = new HashMap<>(); + Map searchData = new HashMap<>(); + requestData.put(Constants.SEARCH, searchData); + requestBody.put(Constants.REQUEST, requestData); + List> questionDataList = new ArrayList<>(); + int chunkSize = 15; + for (int i = 0; i < identifiers.size(); i += chunkSize) { + List identifierList; + if ((i + chunkSize) >= identifiers.size()) { + identifierList = identifiers.subList(i, identifiers.size()); + } else { + identifierList = identifiers.subList(i, i + chunkSize); + } + searchData.put(Constants.IDENTIFIER, identifierList); + Map data = outboundRequestHandlerService.fetchResultUsingPost(sbUrl.toString(), + requestBody, headers); + if (!ObjectUtils.isEmpty(data)) { + questionDataList.add(data); + } + } + return questionDataList; + } catch (Exception e) { + logger.info(String.format("Failed to process the readQuestionDetails. %s", e.getMessage())); + } + return new ArrayList<>(); + } + + @Override + public Map getReadHierarchyApiResponse(String assessmentIdentifier, String token) { + try { + StringBuilder sbUrl = new StringBuilder(serverProperties.getAssessmentHost()); + sbUrl.append(serverProperties.getAssessmentHierarchyReadPath()); + String serviceURL = sbUrl.toString().replace(Constants.IDENTIFIER_REPLACER, assessmentIdentifier); + Map headers = new HashMap<>(); + headers.put(Constants.X_AUTH_TOKEN, token); + headers.put(Constants.AUTHORIZATION, serverProperties.getSbApiKey()); + Object o = outboundRequestHandlerService.fetchUsingGetWithHeaders(serviceURL, headers); + return new ObjectMapper().convertValue(o, Map.class); + } catch (Exception e) { + logger.error(e.getMessage()); + } + return new HashMap<>(); + } } diff --git a/src/main/java/org/sunbird/cache/RedisCacheMgr.java b/src/main/java/org/sunbird/cache/RedisCacheMgr.java deleted file mode 100644 index 318257200..000000000 --- a/src/main/java/org/sunbird/cache/RedisCacheMgr.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.sunbird.cache; - -import java.util.*; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Component; -import org.sunbird.common.util.CbExtServerProperties; -import org.sunbird.common.util.Constants; -import org.sunbird.core.logger.CbExtLogger; - -@Component -public class RedisCacheMgr { - - private static final int cache_ttl = 84600; - - @Autowired - private RedisTemplate redisTemplate; - - @Autowired - CbExtServerProperties cbExtServerProperties; - - private CbExtLogger logger = new CbExtLogger(getClass().getName()); - - public void putCache(String key, Object object) { - try { - int ttl = cache_ttl; - if (!StringUtils.isEmpty(cbExtServerProperties.getRedisTimeout())) { - ttl = Integer.parseInt(cbExtServerProperties.getRedisTimeout()); - } - redisTemplate.opsForValue().set(Constants.REDIS_COMMON_KEY + key, object); - redisTemplate.expire(Constants.REDIS_COMMON_KEY + key, ttl, TimeUnit.SECONDS); - logger.info("Cache_key_value " + Constants.REDIS_COMMON_KEY + key + " is saved in redis"); - } catch (Exception e) { - logger.error(e); - } - } - - public boolean deleteKeyByName(String key) { - try { - redisTemplate.delete(Constants.REDIS_COMMON_KEY + key); - logger.info("Cache_key_value " + Constants.REDIS_COMMON_KEY + key + " is deleted from redis"); - return true; - } catch (Exception e) { - logger.error(e); - return false; - } - } - - public boolean deleteAllCBExtKey() { - try { - String keyPattern = Constants.REDIS_COMMON_KEY + "*"; - Set keys = redisTemplate.keys(keyPattern); - for (String key : keys) { - redisTemplate.delete(key); - } - logger.info("All Keys starts with " + Constants.REDIS_COMMON_KEY + " is deleted from redis"); - return true; - } catch (Exception e) { - logger.error(e); - return false; - } - } - - public Object getCache(String key) { - try { - return redisTemplate.opsForValue().get(Constants.REDIS_COMMON_KEY + key); - } catch (Exception e) { - logger.error(e); - return null; - } - } - - public List mget(List fields) { - try { - List ls = new ArrayList<>(); - for (int i = 0; i < fields.size(); i++) { - ls.add(Constants.REDIS_COMMON_KEY + Constants.QUESTION_ID + fields.get(i)); - } - Collection questionIdList = ls; - return redisTemplate.opsForValue().multiGet(questionIdList); - } catch (Exception e) { - logger.error(e); - } - return null; - } - - public Set getAllKeyNames() { - Set keys = null; - try { - String keyPattern = Constants.REDIS_COMMON_KEY + "*"; - keys = redisTemplate.keys(keyPattern); - } catch (Exception e) { - logger.error(e); - return Collections.emptySet(); - } - return keys; - } - - public List> getAllKeysAndValues() { - List> result = new ArrayList>(); - try { - String keyPattern = Constants.REDIS_COMMON_KEY + "*"; - Map res = new HashMap<>(); - Set keys = redisTemplate.keys(keyPattern); - if (!keys.isEmpty()) { - for (String key : keys) { - Object entries; - entries = redisTemplate.opsForValue().get(key); - res.put(key, entries); - } - result.add(res); - } - } catch (Exception e) { - logger.error(e); - return Collections.emptyList(); - } - return result; - } -} diff --git a/src/main/java/org/sunbird/cache/controller/RedisCacheController.java b/src/main/java/org/sunbird/cache/controller/RedisCacheController.java deleted file mode 100644 index 165de5f75..000000000 --- a/src/main/java/org/sunbird/cache/controller/RedisCacheController.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.sunbird.cache.controller; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.sunbird.cache.service.RedisCacheService; -import org.sunbird.common.model.SBApiResponse; - -@RestController -public class RedisCacheController { - - @Autowired - RedisCacheService redisCacheService; - - - @DeleteMapping("/redis") - public ResponseEntity deleteCache() throws Exception { - SBApiResponse response = redisCacheService.deleteCache(); - return new ResponseEntity<>(response, response.getResponseCode()); - } - - @GetMapping("/redis") - public ResponseEntity getKeys() throws Exception { - SBApiResponse response = redisCacheService.getKeys(); - return new ResponseEntity<>(response, response.getResponseCode()); - } - - @GetMapping("/redis/values") - public ResponseEntity getKeysAndValues() throws Exception { - SBApiResponse response = redisCacheService.getKeysAndValues(); - return new ResponseEntity<>(response, response.getResponseCode()); - } - -} \ No newline at end of file diff --git a/src/main/java/org/sunbird/cache/service/RedisCacheService.java b/src/main/java/org/sunbird/cache/service/RedisCacheService.java deleted file mode 100644 index be065ecf4..000000000 --- a/src/main/java/org/sunbird/cache/service/RedisCacheService.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.sunbird.cache.service; - -import org.sunbird.common.model.SBApiResponse; - -public interface RedisCacheService { - - public SBApiResponse deleteCache() throws Exception; - - public SBApiResponse getKeys() throws Exception; - - public SBApiResponse getKeysAndValues() throws Exception; - -} diff --git a/src/main/java/org/sunbird/cache/service/RedisCacheServiceImpl.java b/src/main/java/org/sunbird/cache/service/RedisCacheServiceImpl.java deleted file mode 100644 index 8e47db0be..000000000 --- a/src/main/java/org/sunbird/cache/service/RedisCacheServiceImpl.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.sunbird.cache.service; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.sunbird.cache.RedisCacheMgr; -import org.sunbird.common.model.SBApiResponse; -import org.sunbird.common.util.Constants; -import org.sunbird.core.logger.CbExtLogger; - -@Service -public class RedisCacheServiceImpl implements RedisCacheService { - - @Autowired - RedisCacheMgr redisCache; - - private CbExtLogger logger = new CbExtLogger(getClass().getName()); - - @Override - public SBApiResponse deleteCache() throws Exception { - SBApiResponse response = new SBApiResponse(Constants.API_REDIS_DELETE); - boolean res = redisCache.deleteAllCBExtKey(); - if (res) { - response.getParams().setStatus(Constants.SUCCESSFUL); - response.setResponseCode(HttpStatus.OK); - } else { - String errMsg = "No Keys found, Redis cache is empty"; - logger.info(errMsg); - response.getParams().setErrmsg(errMsg); - response.setResponseCode(HttpStatus.NOT_FOUND); - } - return response; - } - - @Override - public SBApiResponse getKeys() throws Exception { - SBApiResponse response = new SBApiResponse(Constants.API_REDIS_GET_KEYS); - Set res = redisCache.getAllKeyNames(); - if (!res.isEmpty()) { - logger.info("All Keys in Redis Cache is Fetched"); - response.getParams().setStatus(Constants.SUCCESSFUL); - response.put(Constants.RESPONSE, res); - response.setResponseCode(HttpStatus.OK); - - } else { - String errMsg = "No Keys found, Redis cache is empty"; - logger.info(errMsg); - response.getParams().setErrmsg(errMsg); - response.setResponseCode(HttpStatus.NOT_FOUND); - } - return response; - } - - @Override - public SBApiResponse getKeysAndValues() throws Exception { - SBApiResponse response = new SBApiResponse(Constants.API_REDIS_GET_KEYS_VALUE_SET); - List> result = redisCache.getAllKeysAndValues(); - - if (!result.isEmpty()) { - logger.info("All Keys and Values in Redis Cache is Fetched"); - response.getParams().setStatus(Constants.SUCCESSFUL); - response.put(Constants.RESPONSE, result); - response.setResponseCode(HttpStatus.OK); - } else { - String errMsg = "No Keys found, Redis cache is empty"; - logger.info(errMsg); - response.getParams().setErrmsg(errMsg); - response.setResponseCode(HttpStatus.NOT_FOUND); - } - return response; - } -} diff --git a/src/main/java/org/sunbird/common/util/AccessTokenValidator.java b/src/main/java/org/sunbird/common/util/AccessTokenValidator.java deleted file mode 100644 index 9fd676960..000000000 --- a/src/main/java/org/sunbird/common/util/AccessTokenValidator.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.sunbird.common.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.StringUtils; -import org.keycloak.common.util.Time; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collections; -import java.util.Map; - -public class AccessTokenValidator { - private static Logger logger = LoggerFactory.getLogger(AccessTokenValidator.class.getName()); - private static ObjectMapper mapper = new ObjectMapper(); - - private static Map validateToken(String token) throws Exception { - try { - String[] tokenElements = token.split("\\."); - String header = tokenElements[0]; - String body = tokenElements[1]; - String signature = tokenElements[2]; - String payLoad = header + Constants.DOT_SEPARATOR + body; - Map headerData = - mapper.readValue(new String(decodeFromBase64(header)), Map.class); - String keyId = headerData.get("kid").toString(); - boolean isValid = - CryptoUtil.verifyRSASign( - payLoad, - decodeFromBase64(signature), - KeyManager.getPublicKey(keyId).getPublicKey(), - Constants.SHA_256_WITH_RSA); - if (isValid) { - Map tokenBody = - mapper.readValue(new String(decodeFromBase64(body)), Map.class); - boolean isExp = isExpired((Integer) tokenBody.get("exp")); - if (isExp) { - return Collections.EMPTY_MAP; - } - return tokenBody; - } - } catch (IOException e) { - return Collections.EMPTY_MAP; - } - return Collections.EMPTY_MAP; - } - - - public static String verifyUserToken(String token) { - String userId = Constants._UNAUTHORIZED; - try { - Map payload = validateToken(token); - if (MapUtils.isNotEmpty(payload) && checkIss((String) payload.get("iss"))) { - userId = (String) payload.get(Constants.SUB); - if (StringUtils.isNotBlank(userId)) { - int pos = userId.lastIndexOf(":"); - userId = userId.substring(pos + 1); - } - } - } catch (Exception ex) { - logger.error("Exception in verifyUserAccessToken: verify ", ex); - } - return userId; - } - - private static boolean checkIss(String iss) { - String realmUrl = - KeyCloakConnectionProvider.SSO_URL + "realms/" + KeyCloakConnectionProvider.SSO_REALM; - return (realmUrl.equalsIgnoreCase(iss)); - } - - private static boolean isExpired(Integer expiration) { - return (Time.currentTime() > expiration); - } - - private static byte[] decodeFromBase64(String data) { - return Base64Util.decode(data, 11); - } -} diff --git a/src/main/java/org/sunbird/common/util/CbExtServerProperties.java b/src/main/java/org/sunbird/common/util/CbExtServerProperties.java index 7f64d2a13..2d560ef88 100644 --- a/src/main/java/org/sunbird/common/util/CbExtServerProperties.java +++ b/src/main/java/org/sunbird/common/util/CbExtServerProperties.java @@ -430,6 +430,18 @@ public class CbExtServerProperties { @Value("${course.url}") private String courseLinkUrl; + + @Value("${assessment.use.redis}") + private boolean assessmentUseRedisCache; + + @Value("${kafka.topics.user.assessment.submit}") + private String assessmentSubmitTopic; + + @Value("${sso.url}") + private String ssoUrl; + + @Value("${sso.realm}") + private String ssoRealm; public String getUserAssessmentSubmissionDuration() { return userAssessmentSubmissionDuration; @@ -1573,4 +1585,36 @@ public String getCourseLinkUrl() { public void setCourseLinkUrl(String courseLinkUrl) { this.courseLinkUrl = courseLinkUrl; } + + public boolean isAssessmentUseRedisCache() { + return assessmentUseRedisCache; + } + + public void setAssessmentUseRedisCache(boolean assessmentUseRedisCache) { + this.assessmentUseRedisCache = assessmentUseRedisCache; + } + + public String getAssessmentSubmitTopic() { + return assessmentSubmitTopic; + } + + public void setAssessmentSubmitTopic(String assessmentSubmitTopic) { + this.assessmentSubmitTopic = assessmentSubmitTopic; + } + + public String getSsoUrl() { + return ssoUrl; + } + + public void setSsoUrl(String ssoUrl) { + this.ssoUrl = ssoUrl; + } + + public String getSsoRealm() { + return ssoRealm; + } + + public void setSsoRealm(String ssoRealm) { + this.ssoRealm = ssoRealm; + } } \ 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 21586ebba..6f37733ee 100644 --- a/src/main/java/org/sunbird/common/util/Constants.java +++ b/src/main/java/org/sunbird/common/util/Constants.java @@ -218,7 +218,6 @@ public class Constants { public static final String TABLE_USER_ASSESSMENT_TIME = "user_assessment_time"; public static final String SHA_256_WITH_RSA = "SHA256withRSA"; public static final String SUB = "sub"; - public static final String _UNAUTHORIZED = "Unauthorized"; public static final String DOT_SEPARATOR = "."; public static final String ACCESS_TOKEN_PUBLICKEY_BASEPATH = "accesstoken.publickey.basepath"; public static final String TABLE_ORG_AUDIT = "org_audit"; @@ -260,6 +259,7 @@ public class Constants { public static final String PERSONAL_DETAILS = "personalDetails"; public static final String TRANSITION_DETAILS = "transitionDetails"; public static final String UNAUTHORIZED = "unauthorized"; + public static final String UNAUTHORIZED_KEY = "Unauthorized"; // Redis public static final String API_REDIS_DELETE = "api.redis.delete"; public static final String API_REDIS_GET_KEYS = "api.redis.get.keys"; @@ -530,6 +530,45 @@ public class Constants { public static final String CONTENT_TYPE_SEARCH = "contentType"; public static final String NEW_COURSES = "newcourses"; public static final String OVERVIEW_BATCH_KEY = "/overview?batchId="; + public static final String PRACTICE_QUESTION_SET = "Practice Question Set"; + public static final String EXPECTED_DURATION = "expectedDuration"; + public static final String SUBMITTED = "SUBMITTED"; + public static final String NOT_SUBMITTED = "NOT SUBMITTED"; + public static final String END_TIME = "endtime"; + public static final String ASSESSMENT_ID_KEY = "assessmentId"; + public static final String START_TIME = "starttime"; + public static final String CONTENT_ID_KEY = "contentId"; + public static final String QUESTION_TYPE = "qType"; + public static final String SELECTED_ANSWER = "selectedAnswer"; + public static final String INDEX = "index"; + public static final String MCQ_SCA = "mcq-sca"; + public static final String MCQ_MCA = "mcq-mca"; + public static final String FTB = "ftb"; + public static final String MTF = "mtf"; + public static final String IS_CORRECT = "isCorrect"; + public static final String OPTION_ID = "optionId"; + + public static final String TABLE_USER_ASSESSMENT_DATA = "user_assessment_data"; + + + public static final String USER_ID_DOESNT_EXIST = "User Id doesn't exist! Please supply a valid auth token"; + public static final String ASSESSMENT_DATA_START_TIME_NOT_UPDATED = "Assessment Data & Start Time not updated in the DB! Please check!"; + public static final String ASSESSMENT_HIERARCHY_READ_FAILED = "Assessment hierarchy read failed, failed to process request"; + public static final String ASSESSMENT_ID_KEY_IS_NOT_PRESENT_IS_EMPTY = "Assessment Id Key is not present/is empty"; + public static final String IDENTIFIER_LIST_IS_EMPTY = "Identifier List is Empty"; + public static final String THE_QUESTIONS_IDS_PROVIDED_DONT_MATCH = "The Questions Ids Provided don't match the active user assessment session"; + public static final String ASSESSMENT_ID_INVALID_SESSION_EXPIRED = "Assessment Id Invalid/Session Expired/Redis Cache doesn't have this question list details"; + public static final String INVALID_ASSESSMENT_ID = "Invalid Assessment Id"; + public static final String READ_ASSESSMENT_FAILED = "Failed to read assessment hierarchy for the given AssessmentId."; + public static final String READ_ASSESSMENT_START_TIME_FAILED = "Failed to read the assessment start time."; + public static final String WRONG_SECTION_DETAILS = "Wrong section details."; + public static final String ASSESSMENT_SUBMIT_EXPIRED = "The Assessment submission time-period is over! Assessment can't be submitted"; + public static final String ASSESSMENT_SUBMIT_INVALID_QUESTION = "The QuestionId provided don't match to the Assessment Read"; + public static final String ASSESSMENT_SUBMIT_QUESTION_READ_FAILED = "Failed to read Question Set from DB"; + + + public static final String ASSESSMENT_READ_RESPONSE = "assessmentreadresponse"; + public static final String API_SUBMIT_ASSESSMENT = "api.submit.asssessment"; private Constants() { throw new IllegalStateException("Utility class"); diff --git a/src/main/java/org/sunbird/common/util/KeyCloakConnectionProvider.java b/src/main/java/org/sunbird/common/util/KeyCloakConnectionProvider.java deleted file mode 100644 index a122c3ca8..000000000 --- a/src/main/java/org/sunbird/common/util/KeyCloakConnectionProvider.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.sunbird.common.util; - -import org.apache.commons.lang3.StringUtils; -import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder; -import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.KeycloakBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * @author Manzarul This class will connect to key cloak server and provide the connection to do - * other operations. - */ -public class KeyCloakConnectionProvider { - - private static Logger logger = LoggerFactory.getLogger(KeyCloakConnectionProvider.class.getName()); - - private static Keycloak keycloak; - private static PropertiesCache cache = PropertiesCache.getInstance(); - public static String SSO_URL = null; - public static String SSO_REALM = null; - public static String CLIENT_ID = null; - - static { - try { - initialiseConnection(); - } catch (Exception e) { - logger.error( - "Exception occurred while initializing keycloak connection: " + e.getMessage(), e); - } - registerShutDownHook(); - } - - /** - * Method to initializate the Keycloak connection - * - * @return Keycloak connection - */ - public static Keycloak initialiseConnection() throws Exception { - keycloak = initialiseEnvConnection(); - if (keycloak != null) { - return keycloak; - } - KeycloakBuilder keycloakBuilder = - KeycloakBuilder.builder() - .serverUrl(cache.getProperty(Constants.SSO_URL)) - .realm(cache.getProperty(Constants.SSO_REALM)) - .username(cache.getProperty(Constants.SSO_USERNAME)) - .password(cache.getProperty(Constants.SSO_PASSWORD)) - .clientId(cache.getProperty(Constants.SSO_CLIENT_ID)) - .resteasyClient( - new ResteasyClientBuilder() - .connectionPoolSize(Integer.parseInt(cache.getProperty(Constants.SSO_POOL_SIZE))) - .build()); - if (cache.getProperty(Constants.SSO_CLIENT_SECRET) != null - && !(cache.getProperty(Constants.SSO_CLIENT_SECRET).equals(Constants.SSO_CLIENT_SECRET))) { - keycloakBuilder.clientSecret(cache.getProperty(Constants.SSO_CLIENT_SECRET)); - } - SSO_URL = cache.getProperty(Constants.SSO_URL); - SSO_REALM = cache.getProperty(Constants.SSO_REALM); - CLIENT_ID = cache.getProperty(Constants.SSO_CLIENT_ID); - keycloak = keycloakBuilder.build(); - - logger.info("key cloak instance is created successfully."); - return keycloak; - } - - /** - * This method will provide the keycloak connection from environment variable. if environment - * variable is not set then it will return null. - * - * @return Keycloak - */ - private static Keycloak initialiseEnvConnection() throws Exception { - String url = System.getenv(Constants.SUNBIRD_SSO_URL); - String username = System.getenv(Constants.SUNBIRD_SSO_USERNAME); - String password = System.getenv(Constants.SUNBIRD_SSO_PASSWORD); - String cleintId = System.getenv(Constants.SUNBIRD_SSO_CLIENT_ID); - String clientSecret = System.getenv(Constants.SUNBIRD_SSO_CLIENT_SECRET); - String relam = System.getenv(Constants.SUNBIRD_SSO_RELAM); - if (StringUtils.isBlank(url) - || StringUtils.isBlank(username) - || StringUtils.isBlank(password) - || StringUtils.isBlank(cleintId) - || StringUtils.isBlank(relam)) { - logger.info("key cloak connection is not provided by Environment variable."); - return null; - } - SSO_URL = url; - SSO_REALM = relam; - CLIENT_ID = cleintId; - KeycloakBuilder keycloakBuilder = - KeycloakBuilder.builder() - .serverUrl(url) - .realm(relam) - .username(username) - .password(password) - .clientId(cleintId) - .resteasyClient( - new ResteasyClientBuilder() - .connectionPoolSize(Integer.parseInt(cache.getProperty(Constants.SSO_POOL_SIZE))) - .build()); - - if (StringUtils.isNotBlank(clientSecret)) { - keycloakBuilder.clientSecret(clientSecret); - logger.info("KeyCloakConnectionProvider:initialiseEnvConnection client sceret is provided."); - } - keycloakBuilder.grantType("client_credentials"); - keycloak = keycloakBuilder.build(); - logger.info("key cloak instance is created from Environment variable settings ."); - return keycloak; - } - - /** - * This method will provide key cloak connection instance. - * - * @return Keycloak - */ - public static Keycloak getConnection() { - if (keycloak != null) { - return keycloak; - } else { - try { - return initialiseConnection(); - } catch (Exception e) { - logger.error("getConnection : " + e.getMessage(), e); - } - } - return null; - } - - /** - * This class will be called by registerShutDownHook to register the call inside jvm , when jvm - * terminate it will call the run method to clean up the resource. - * - * @author Manzarul - */ - static class ResourceCleanUp extends Thread { - public void run() { - if (null != keycloak) { - keycloak.close(); - } - } - } - - /** Register the hook for resource clean up. this will be called when jvm shut down. */ - public static void registerShutDownHook() { - Runtime runtime = Runtime.getRuntime(); - runtime.addShutdownHook(new ResourceCleanUp()); - } -} \ No newline at end of file diff --git a/src/main/java/org/sunbird/common/util/ProjectUtil.java b/src/main/java/org/sunbird/common/util/ProjectUtil.java index 2fc08ea61..1847fead8 100644 --- a/src/main/java/org/sunbird/common/util/ProjectUtil.java +++ b/src/main/java/org/sunbird/common/util/ProjectUtil.java @@ -1,7 +1,5 @@ package org.sunbird.common.util; -import com.fasterxml.jackson.databind.ObjectMapper; - import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @@ -26,7 +24,6 @@ public class ProjectUtil { public static CbExtLogger logger = new CbExtLogger(ProjectUtil.class.getName()); public static PropertiesCache propertiesCache; - private static final ObjectMapper mapper = new ObjectMapper(); static { propertiesCache = PropertiesCache.getInstance(); diff --git a/src/main/java/org/sunbird/common/util/RequestInterceptor.java b/src/main/java/org/sunbird/common/util/RequestInterceptor.java index 9445a4176..24da4b982 100644 --- a/src/main/java/org/sunbird/common/util/RequestInterceptor.java +++ b/src/main/java/org/sunbird/common/util/RequestInterceptor.java @@ -1,32 +1,95 @@ package org.sunbird.common.util; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang.StringUtils; +import org.keycloak.common.util.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; -import java.util.concurrent.ConcurrentHashMap; +import com.fasterxml.jackson.databind.ObjectMapper; +@Component public class RequestInterceptor { + private Logger logger = LoggerFactory.getLogger(RequestInterceptor.class.getName()); + private ObjectMapper mapper = new ObjectMapper(); + + @Autowired + CbExtServerProperties serverProperties; + + public String fetchUserIdFromAccessToken(String accessToken) { + String clientAccessTokenId = null; + if (StringUtils.isNotBlank(accessToken)) { + try { + clientAccessTokenId = verifyUserToken(accessToken); + if (Constants.UNAUTHORIZED_KEY.equalsIgnoreCase(clientAccessTokenId)) { + clientAccessTokenId = null; + } + } catch (Exception ex) { + logger.error("Exception occurred while fetching the userid from the access token. Exception: " + + ex.getMessage(), ex); + } + } + return clientAccessTokenId; + } + + private String verifyUserToken(String token) { + String userId = Constants.UNAUTHORIZED_KEY; + try { + Map payload = validateToken(token); + if (MapUtils.isNotEmpty(payload) && checkIss((String) payload.get("iss"))) { + userId = (String) payload.get(Constants.SUB); + if (StringUtils.isNotBlank(userId)) { + int pos = userId.lastIndexOf(":"); + userId = userId.substring(pos + 1); + } + } + } catch (Exception ex) { + logger.error("Exception in verifyUserAccessToken: verify ", ex); + } + return userId; + } + + private Map validateToken(String token) throws Exception { + try { + String[] tokenElements = token.split("\\."); + String header = tokenElements[0]; + String body = tokenElements[1]; + String signature = tokenElements[2]; + String payLoad = header + Constants.DOT_SEPARATOR + body; + Map headerData = mapper.readValue(new String(decodeFromBase64(header)), Map.class); + String keyId = headerData.get("kid").toString(); + boolean isValid = CryptoUtil.verifyRSASign(payLoad, decodeFromBase64(signature), + KeyManager.getPublicKey(keyId).getPublicKey(), Constants.SHA_256_WITH_RSA); + if (isValid) { + Map tokenBody = mapper.readValue(new String(decodeFromBase64(body)), Map.class); + boolean isExp = isExpired((Integer) tokenBody.get("exp")); + if (isExp) { + return Collections.emptyMap(); + } + return tokenBody; + } + } catch (IOException e) { + return Collections.emptyMap(); + } + return Collections.emptyMap(); + } + + private boolean checkIss(String iss) { + String realmUrl = serverProperties.getSsoUrl() + "realms/" + serverProperties.getSsoRealm(); + return (realmUrl.equalsIgnoreCase(iss)); + } + + private boolean isExpired(Integer expiration) { + return (Time.currentTime() > expiration); + } - private static Logger logger = LoggerFactory.getLogger(RequestInterceptor.class.getName()); - private static ConcurrentHashMap apiHeaderIgnoreMap = new ConcurrentHashMap<>(); - - private RequestInterceptor() { - } - - public static String fetchUserIdFromAccessToken(String accessToken) { - String clientAccessTokenId = null; - if (accessToken != null) { - try { - clientAccessTokenId = AccessTokenValidator.verifyUserToken(accessToken); - if (Constants._UNAUTHORIZED.equalsIgnoreCase(clientAccessTokenId)) { - clientAccessTokenId = null; - } - } catch (Exception ex) { - String errMsg = "Exception occurred while fetching the userid from the access token. Exception: " + ex.getMessage(); - logger.error(errMsg, ex); - clientAccessTokenId = null; - } - } - return clientAccessTokenId; - } + private byte[] decodeFromBase64(String data) { + return Base64Util.decode(data, 11); + } } \ No newline at end of file diff --git a/src/main/java/org/sunbird/core/config/ConsumerConfiguration.java b/src/main/java/org/sunbird/core/config/ConsumerConfiguration.java index ec6faacd6..6d9887618 100644 --- a/src/main/java/org/sunbird/core/config/ConsumerConfiguration.java +++ b/src/main/java/org/sunbird/core/config/ConsumerConfiguration.java @@ -45,7 +45,6 @@ KafkaListenerContainerFactory @Bean public ConsumerFactory consumerFactory() { return new DefaultKafkaConsumerFactory<>(consumerConfigs()); - } @Bean diff --git a/src/main/java/org/sunbird/core/config/RedisConfig.java b/src/main/java/org/sunbird/core/config/RedisConfig.java deleted file mode 100644 index 9ffad7d7c..000000000 --- a/src/main/java/org/sunbird/core/config/RedisConfig.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.sunbird.core.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisStandaloneConfiguration; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; -import org.springframework.data.redis.serializer.StringRedisSerializer; -import org.sunbird.common.util.CbExtServerProperties; - -@Configuration -@EnableCaching -public class RedisConfig { - - @Autowired - CbExtServerProperties cbProperties; - - @Bean - public JedisConnectionFactory jedisConnectionFactory() { - RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); - redisStandaloneConfiguration.setHostName(cbProperties.getRedisHostName()); - redisStandaloneConfiguration.setPort(Integer.parseInt(cbProperties.getRedisPort())); - - JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); - return jedisConnectionFactory; - } - - @Bean - public RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(jedisConnectionFactory()); - redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setHashKeySerializer(new JdkSerializationRedisSerializer()); - redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); - redisTemplate.setEnableTransactionSupport(true); - redisTemplate.afterPropertiesSet(); - return redisTemplate; - } -} diff --git a/src/main/java/org/sunbird/course/service/ExploreCourseServiceImpl.java b/src/main/java/org/sunbird/course/service/ExploreCourseServiceImpl.java index 003369a8a..7c6cb1ac4 100644 --- a/src/main/java/org/sunbird/course/service/ExploreCourseServiceImpl.java +++ b/src/main/java/org/sunbird/course/service/ExploreCourseServiceImpl.java @@ -13,8 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; -import org.springframework.util.ObjectUtils; -import org.sunbird.cache.RedisCacheMgr; import org.sunbird.cassandra.utils.CassandraOperation; import org.sunbird.common.model.SBApiResponse; import org.sunbird.common.service.OutboundRequestHandlerServiceImpl; @@ -39,9 +37,6 @@ public class ExploreCourseServiceImpl implements ExploreCourseService { @Autowired CassandraOperation cassandraOperation; - @Autowired - RedisCacheMgr redisCacheMgr; - @Autowired CbExtServerProperties serverProperties; @@ -82,7 +77,6 @@ public SBApiResponse getExploreCourseList() { } public SBApiResponse refreshCache() { - redisCacheMgr.deleteKeyByName(Constants.PUBLIC_COURSE_LIST); SBApiResponse response = getExploreCourseList(); response.setId(Constants.API_REFRESH_EXPLORE_COURSE_DETAIL); return response; diff --git a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java index 2c24b86df..c7b2a3ccb 100644 --- a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java +++ b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java @@ -26,7 +26,6 @@ import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; import org.springframework.web.multipart.MultipartFile; -import org.sunbird.cache.RedisCacheMgr; import org.sunbird.cassandra.utils.CassandraOperation; import org.sunbird.common.model.SBApiResponse; import org.sunbird.common.model.SunbirdApiRespParam; @@ -53,9 +52,6 @@ public class ProfileServiceImpl implements ProfileService { @Autowired OutboundRequestHandlerServiceImpl outboundRequestHandlerService; - @Autowired - RedisCacheMgr redisCacheMgr; - @Autowired UserUtilityServiceImpl userUtilityService; @@ -669,28 +665,16 @@ public SBApiResponse getBulkUploadDetails(String orgId) { } public List approvalFields() { - Map approvalFieldsCache = (Map) mapper - .convertValue(redisCacheMgr.getCache(Constants.PROFILE_UPDATE_FIELDS), Map.class); - - if (!ObjectUtils.isEmpty(approvalFieldsCache)) { - Map approvalResult = (Map) approvalFieldsCache.get(Constants.RESULT); - Map approvalResponse = (Map) approvalResult.get(Constants.RESPONSE); - String value = (String) approvalResponse.get(Constants.VALUE); - List approvalValues = new ArrayList<>(); - approvalValues.add(value); - return approvalValues; - } else { - Map header = new HashMap<>(); - Map approvalData = (Map) outboundRequestHandlerService - .fetchUsingGetWithHeadersProfile(serverConfig.getSbUrl() + serverConfig.getLmsSystemSettingsPath(), - header); - Map approvalResult = (Map) approvalData.get(Constants.RESULT); - Map approvalResponse = (Map) approvalResult.get(Constants.RESPONSE); - String value = (String) approvalResponse.get(Constants.VALUE); - String strArray[] = value.split(" "); - List approvalValues = Arrays.asList(strArray); - return approvalValues; - } + Map header = new HashMap<>(); + Map approvalData = (Map) outboundRequestHandlerService + .fetchUsingGetWithHeadersProfile(serverConfig.getSbUrl() + serverConfig.getLmsSystemSettingsPath(), + header); + Map approvalResult = (Map) approvalData.get(Constants.RESULT); + Map approvalResponse = (Map) approvalResult.get(Constants.RESPONSE); + String value = (String) approvalResponse.get(Constants.VALUE); + String strArray[] = value.split(" "); + List approvalValues = Arrays.asList(strArray); + return approvalValues; } public String checkDepartment(Map requestProfile) throws Exception { @@ -772,37 +756,29 @@ private SBApiResponse createDefaultResponse(String api) { } public String getCustodianOrgId() { - String custodianOrgId = (String) redisCacheMgr.getCache(Constants.CUSTODIAN_ORG_ID); - if (StringUtils.isEmpty(custodianOrgId)) { - Map searchRequest = new HashMap(); - searchRequest.put(Constants.ID, Constants.CUSTODIAN_ORG_ID); - - List> existingDataList = cassandraOperation.getRecordsByProperties( - Constants.KEYSPACE_SUNBIRD, Constants.TABLE_SYSTEM_SETTINGS, searchRequest, null); - if (CollectionUtils.isNotEmpty(existingDataList)) { - Map data = existingDataList.get(0); - custodianOrgId = (String) data.get(Constants.VALUE.toLowerCase()); - } - redisCacheMgr.putCache(Constants.CUSTODIAN_ORG_ID, custodianOrgId); + Map searchRequest = new HashMap(); + searchRequest.put(Constants.ID, Constants.CUSTODIAN_ORG_ID); + + List> existingDataList = cassandraOperation.getRecordsByProperties( + Constants.KEYSPACE_SUNBIRD, Constants.TABLE_SYSTEM_SETTINGS, searchRequest, null); + if (CollectionUtils.isNotEmpty(existingDataList)) { + Map data = existingDataList.get(0); + return (String) data.get(Constants.VALUE.toLowerCase()); } - return custodianOrgId; + return StringUtils.EMPTY; } public String getCustodianOrgChannel() { - String custodianOrgChannel = (String) redisCacheMgr.getCache(Constants.CUSTODIAN_ORG_CHANNEL); - if (StringUtils.isEmpty(custodianOrgChannel)) { - Map searchRequest = new HashMap(); - searchRequest.put(Constants.ID, Constants.CUSTODIAN_ORG_CHANNEL); - - List> existingDataList = cassandraOperation.getRecordsByProperties( - Constants.KEYSPACE_SUNBIRD, Constants.TABLE_SYSTEM_SETTINGS, searchRequest, null); - if (CollectionUtils.isNotEmpty(existingDataList)) { - Map data = existingDataList.get(0); - custodianOrgChannel = (String) data.get(Constants.VALUE.toLowerCase()); - } - redisCacheMgr.putCache(Constants.CUSTODIAN_ORG_CHANNEL, custodianOrgChannel); + Map searchRequest = new HashMap(); + searchRequest.put(Constants.ID, Constants.CUSTODIAN_ORG_CHANNEL); + + List> existingDataList = cassandraOperation.getRecordsByProperties( + Constants.KEYSPACE_SUNBIRD, Constants.TABLE_SYSTEM_SETTINGS, searchRequest, null); + if (CollectionUtils.isNotEmpty(existingDataList)) { + Map data = existingDataList.get(0); + return (String) data.get(Constants.VALUE.toLowerCase()); } - return custodianOrgChannel; + return StringUtils.EMPTY; } private String validateBasicProfilePayload(Map requestObj) { diff --git a/src/main/java/org/sunbird/searchby/service/SearchByService.java b/src/main/java/org/sunbird/searchby/service/SearchByService.java index 935c889f5..fff455d77 100644 --- a/src/main/java/org/sunbird/searchby/service/SearchByService.java +++ b/src/main/java/org/sunbird/searchby/service/SearchByService.java @@ -13,8 +13,6 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; -import org.sunbird.cache.RedisCacheMgr; import org.sunbird.common.model.FracApiResponse; import org.sunbird.common.service.OutboundRequestHandlerServiceImpl; import org.sunbird.common.util.CbExtServerProperties; @@ -37,32 +35,19 @@ public class SearchByService { @Autowired CbExtServerProperties cbExtServerProperties; - @Autowired - RedisCacheMgr redisCacheMgr; - @Autowired OutboundRequestHandlerServiceImpl outboundRequestHandlerService; public Collection getCompetencyDetails(String authUserToken) throws Exception { - Map competencyMap = (Map) redisCacheMgr - .getCache(Constants.COMPETENCY_CACHE_NAME); - - if (CollectionUtils.isEmpty(competencyMap)) { - logger.info("Initializing/Refreshing the Cache Value for Key : " + Constants.COMPETENCY_CACHE_NAME); - competencyMap = updateCompetencyDetails(authUserToken); - } + logger.info("Initializing/Refreshing the Cache Value for Key : " + Constants.COMPETENCY_CACHE_NAME); + Map competencyMap = updateCompetencyDetails(authUserToken); return competencyMap.values(); } public Collection getProviderDetails(String authUserToken) throws Exception { - Map providerMap = (Map) redisCacheMgr - .getCache(Constants.PROVIDER_CACHE_NAME); - - if (CollectionUtils.isEmpty(providerMap)) { - logger.info("Initializing/Refreshing the Cache Value for Key : " + Constants.PROVIDER_CACHE_NAME); - providerMap = updateProviderDetails(authUserToken); - } + logger.info("Initializing/Refreshing the Cache Value for Key : " + Constants.PROVIDER_CACHE_NAME); + Map providerMap = updateProviderDetails(authUserToken); return providerMap.values(); } @@ -71,21 +56,14 @@ public FracApiResponse listPositions(String userToken) { response.setStatusInfo(new FracStatusInfo()); response.getStatusInfo().setStatusCode(HttpStatus.OK.value()); - Map> positionMap = (Map>) redisCacheMgr - .getCache(Constants.POSITIONS_CACHE_NAME); - if (ObjectUtils.isEmpty(positionMap) - || CollectionUtils.isEmpty(positionMap.get(Constants.POSITIONS_CACHE_NAME))) { - logger.info("Initializing / Refreshing the Cache value for key : " + Constants.POSITIONS_CACHE_NAME); - try { - positionMap = updateDesignationDetails(userToken); - response.setResponseData(positionMap.get(Constants.POSITIONS_CACHE_NAME)); - } catch (Exception e) { - logger.error(e); - response.getStatusInfo().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); - response.getStatusInfo().setErrorMessage(e.getMessage()); - } - } else { + logger.info("Initializing / Refreshing the Cache value for key : " + Constants.POSITIONS_CACHE_NAME); + try { + Map> positionMap = updateDesignationDetails(userToken); response.setResponseData(positionMap.get(Constants.POSITIONS_CACHE_NAME)); + } catch (Exception e) { + logger.error(e); + response.getStatusInfo().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.getStatusInfo().setErrorMessage(e.getMessage()); } return response; @@ -221,10 +199,6 @@ private Map updateCompetencyDetails(String authUserToken throw err; } - redisCacheMgr.putCache(Constants.COMPETENCY_CACHE_NAME, competencyMap); - redisCacheMgr.putCache(Constants.COMPETENCY_CACHE_NAME_BY_TYPE, comInfoByType); - redisCacheMgr.putCache(Constants.COMPETENCY_CACHE_NAME_BY_AREA, comInfoByArea); - return competencyMap; } @@ -319,7 +293,6 @@ private Map updateProviderDetails(String authUserToken) th throw err; } - redisCacheMgr.putCache(Constants.PROVIDER_CACHE_NAME, providerMap); return providerMap; } @@ -370,7 +343,6 @@ private Map> updateDesignationDetails(String authUs } Map> positionMap = new HashMap>(); positionMap.put(Constants.POSITIONS_CACHE_NAME, positionList); - redisCacheMgr.putCache(Constants.POSITIONS_CACHE_NAME, positionMap); return positionMap; } diff --git a/src/main/java/org/sunbird/user/registration/service/UserRegistrationServiceImpl.java b/src/main/java/org/sunbird/user/registration/service/UserRegistrationServiceImpl.java index 592cd9924..adec0a789 100644 --- a/src/main/java/org/sunbird/user/registration/service/UserRegistrationServiceImpl.java +++ b/src/main/java/org/sunbird/user/registration/service/UserRegistrationServiceImpl.java @@ -28,9 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; -import org.springframework.util.ObjectUtils; import org.springframework.web.client.RestTemplate; -import org.sunbird.cache.RedisCacheMgr; import org.sunbird.common.model.SBApiResponse; import org.sunbird.common.model.SunbirdApiRequest; import org.sunbird.common.model.SunbirdApiResp; @@ -77,9 +75,6 @@ public class UserRegistrationServiceImpl implements UserRegistrationService { @Autowired UserUtilityService userUtilityService; - @Autowired - RedisCacheMgr redisCacheMgr; - @Autowired ExtendedOrgService extOrgService; @@ -169,15 +164,7 @@ public SBApiResponse getDeptDetails() { SBApiResponse response = createDefaultResponse(Constants.USER_REGISTRATION_DEPT_INFO_API); try { - Map> deptListMap = (Map>) redisCacheMgr - .getCache(Constants.DEPARTMENT_LIST_CACHE_NAME); - List orgList = null; - if (ObjectUtils.isEmpty(deptListMap) - || CollectionUtils.isEmpty(deptListMap.get(Constants.DEPARTMENT_LIST_CACHE_NAME))) { - orgList = getDepartmentDetails(); - } else { - orgList = deptListMap.get(Constants.DEPARTMENT_LIST_CACHE_NAME); - } + List orgList = getDepartmentDetails(); response.getResult().put(Constants.COUNT, orgList.size()); response.getResult().put(Constants.CONTENT, orgList); } catch (Exception e) { @@ -488,9 +475,6 @@ private List getDepartmentDetails() throws Exception { throw new Exception("Failed to retrieve organisation details."); } - Map> deptListMap = new HashMap>(); - deptListMap.put(Constants.DEPARTMENT_LIST_CACHE_NAME, orgList); - redisCacheMgr.putCache(Constants.DEPARTMENT_LIST_CACHE_NAME, deptListMap); return orgList; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index d67d4a82e..aba381b73 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -181,7 +181,7 @@ notification.service.host=http://notification-service:9000 last.access.time.gap.millis=259200000 cassandra.config.host=localhost -sso.url=https://igot-dev.in/auth/ +sso.url=https://portal.igot-dev.in/auth/ sso.realm=sunbird sso.connection.pool.size=20 sso.enabled=true @@ -263,3 +263,6 @@ latest.courses.alert.search.content.fields=identifier,name,posterImage,duration, latest.courses.alert.email.subject=Check out exciting new courses that launched this week! latest.courses.alert.scheduler.time.gap=100 latest.courses.alert.content.min.limit=1 + +assessment.use.redis=false +kafka.topics.user.assessment.submit=assessment.submit From feca94eb0715fb6a000030778eee967df357bd53 Mon Sep 17 00:00:00 2001 From: Juhi Date: Mon, 14 Nov 2022 13:20:19 +0530 Subject: [PATCH 02/13] changes --- .../service/AssessmentServiceV2Impl.java | 37 ++++++++++++++----- .../service/AssessmentUtilServiceV2.java | 2 +- .../service/AssessmentUtilServiceV2Impl.java | 11 ++++-- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java index 1b2cceaae..8b0350b83 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java @@ -133,12 +133,17 @@ public SBApiResponse readAssessment(String assessmentIdentifier, String token) { public SBApiResponse readQuestionList(Map requestBody, String authUserToken) { SBApiResponse response = createDefaultResponse(Constants.API_SUBMIT_ASSESSMENT); String errMsg; + String primaryCategory = ""; + Map result = new HashMap<>(); try { List identifierList = new ArrayList<>(); List questionList = new ArrayList<>(); - errMsg = validateQuestionListAPI(requestBody, authUserToken, identifierList); + result = validateQuestionListAPI(requestBody, authUserToken, identifierList); + errMsg = result.get(Constants.ERROR_MESSAGE); + if(result.containsKey(Constants.PRIMARY_CATEGORY) && result.get(Constants.PRIMARY_CATEGORY).equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) + primaryCategory = result.get(Constants.PRIMARY_CATEGORY); if (errMsg.isEmpty()) { - errMsg = assessUtilServ.fetchQuestionIdentifierValue(identifierList, questionList); + errMsg = assessUtilServ.fetchQuestionIdentifierValue(identifierList, questionList, primaryCategory); if (errMsg.isEmpty() && identifierList.size() == questionList.size()) { response.getResult().put(Constants.QUESTIONS, questionList); } @@ -175,20 +180,24 @@ private String fetchReadHierarchyDetails(Map assessmentAllDetail return StringUtils.EMPTY; } - private String validateQuestionListAPI(Map requestBody, String authUserToken, + private Map validateQuestionListAPI(Map requestBody, String authUserToken, List identifierList) { + Map result = new HashMap<>(); String userId = validateAuthTokenAndFetchUserId(authUserToken); if (StringUtils.isBlank(userId)) { - return Constants.USER_ID_DOESNT_EXIST; + result.put(Constants.ERROR_MESSAGE, Constants.USER_ID_DOESNT_EXIST); + return result; } if (StringUtils.isBlank((String) requestBody.get(Constants.ASSESSMENT_ID_KEY))) { - return Constants.ASSESSMENT_ID_KEY_IS_NOT_PRESENT_IS_EMPTY; + result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_ID_KEY_IS_NOT_PRESENT_IS_EMPTY); + return result; } identifierList.addAll(getQuestionIdList(requestBody)); if (identifierList.isEmpty()) { - return Constants.IDENTIFIER_LIST_IS_EMPTY; + result.put(Constants.ERROR_MESSAGE, Constants.IDENTIFIER_LIST_IS_EMPTY); + return result; } Map assessmentDetail = new HashMap<>(); @@ -196,7 +205,8 @@ private String validateQuestionListAPI(Map requestBody, String a (String) requestBody.get(Constants.ASSESSMENT_ID_KEY)); if (ObjectUtils.isEmpty(assessmentDetail)) { - return Constants.ASSESSMENT_HIERARCHY_READ_FAILED; + result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_HIERARCHY_READ_FAILED); + return result; } if (!((String) assessmentDetail.get(Constants.PRIMARY_CATEGORY)) @@ -220,13 +230,20 @@ private String validateQuestionListAPI(Map requestBody, String a // has only those ids which are a part of the user's latest assessment // Fetching all the remaining questions details from the Redis if (Boolean.FALSE.equals(validateQuestionListRequest(identifierList, questionsFromAssessment))) { - return Constants.THE_QUESTIONS_IDS_PROVIDED_DONT_MATCH; + result.put(Constants.ERROR_MESSAGE, Constants.THE_QUESTIONS_IDS_PROVIDED_DONT_MATCH); + return result; } } else { - return Constants.ASSESSMENT_ID_INVALID_SESSION_EXPIRED; + result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_ID_INVALID_SESSION_EXPIRED); + return result; } } - return ""; + else + { + result.put(Constants.PRIMARY_CATEGORY, Constants.PRACTICE_QUESTION_SET); + } + result.put(Constants.ERROR_MESSAGE, ""); + return result; } @Override diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java index a786eccd1..020c3c4e0 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2.java @@ -7,7 +7,7 @@ public interface AssessmentUtilServiceV2 { public Map validateQumlAssessment(List originalQuestionList, List> userQuestionList); - public String fetchQuestionIdentifierValue(List identifierList, List questionList) throws Exception; + public String fetchQuestionIdentifierValue(List identifierList, List questionList, String primaryCategory) throws Exception; public Map getReadHierarchyApiResponse(String assessmentIdentifier, String token); } diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java index 3f6557a8d..106e80f6b 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentUtilServiceV2Impl.java @@ -185,7 +185,7 @@ private void fetchQuestionMapDetails(List questions, Map identifierList, List questionList) + public String fetchQuestionIdentifierValue(List identifierList, List questionList, String primaryCategory) throws Exception { List newIdentifierList = new ArrayList<>(); newIdentifierList.addAll(identifierList); @@ -201,7 +201,7 @@ public String fetchQuestionIdentifierValue(List identifierList, List question : questionMap) { if (!ObjectUtils.isEmpty(questionMap)) { - questionList.add(filterQuestionMapDetail(question)); + questionList.add(filterQuestionMapDetail(question, primaryCategory)); } else { logger.error(String.format("Failed to get Question Details for Id: %s", question.get(Constants.IDENTIFIER).toString())); @@ -219,7 +219,7 @@ public String fetchQuestionIdentifierValue(List identifierList, List filterQuestionMapDetail(Map questionMapResponse) { + private Map filterQuestionMapDetail(Map questionMapResponse, String primaryCategory) { List questionParams = serverProperties.getAssessmentQuestionParams(); Map updatedQuestionMap = new HashMap<>(); for (String questionParam : questionParams) { @@ -227,6 +227,11 @@ private Map filterQuestionMapDetail(Map question 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) && !updatedQuestionMap .get(Constants.PRIMARY_CATEGORY).toString().equalsIgnoreCase(Constants.FTB_QUESTION)) { From 0b21f3570322cef5cfc2f3529be341b8062b4095 Mon Sep 17 00:00:00 2001 From: Juhi Date: Mon, 14 Nov 2022 13:21:34 +0530 Subject: [PATCH 03/13] changes --- .../sunbird/assessment/service/AssessmentServiceV2Impl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java index 8b0350b83..f54903ea7 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java @@ -140,9 +140,9 @@ public SBApiResponse readQuestionList(Map requestBody, String au List questionList = new ArrayList<>(); result = validateQuestionListAPI(requestBody, authUserToken, identifierList); errMsg = result.get(Constants.ERROR_MESSAGE); - if(result.containsKey(Constants.PRIMARY_CATEGORY) && result.get(Constants.PRIMARY_CATEGORY).equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) - primaryCategory = result.get(Constants.PRIMARY_CATEGORY); if (errMsg.isEmpty()) { + if(result.containsKey(Constants.PRIMARY_CATEGORY) && result.get(Constants.PRIMARY_CATEGORY).equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) + primaryCategory = result.get(Constants.PRIMARY_CATEGORY); errMsg = assessUtilServ.fetchQuestionIdentifierValue(identifierList, questionList, primaryCategory); if (errMsg.isEmpty() && identifierList.size() == questionList.size()) { response.getResult().put(Constants.QUESTIONS, questionList); From 2bbe01b77443cb9046a5d37baa60e94463f4ab1d Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 18 Nov 2022 11:23:51 +0530 Subject: [PATCH 04/13] 4.0.1 user notify (#153) * Update Notification preference changes (#151) Co-authored-by: Manas-tarento <107806230+Manas-tarento@users.noreply.github.com> --- sb-cb-ext.iml | 379 ------------------ .../common/util/CbExtServerProperties.java | 11 + .../org/sunbird/common/util/Constants.java | 7 + .../profile/controller/ProfileController.java | 13 + .../profile/service/ProfileService.java | 5 + .../profile/service/ProfileServiceImpl.java | 88 +++- src/main/resources/application.properties | 1 + 7 files changed, 124 insertions(+), 380 deletions(-) delete mode 100644 sb-cb-ext.iml diff --git a/sb-cb-ext.iml b/sb-cb-ext.iml deleted file mode 100644 index 9f592f5e6..000000000 --- a/sb-cb-ext.iml +++ /dev/null @@ -1,379 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/java/org/sunbird/common/util/CbExtServerProperties.java b/src/main/java/org/sunbird/common/util/CbExtServerProperties.java index 2d560ef88..aed16e882 100644 --- a/src/main/java/org/sunbird/common/util/CbExtServerProperties.java +++ b/src/main/java/org/sunbird/common/util/CbExtServerProperties.java @@ -442,6 +442,9 @@ public class CbExtServerProperties { @Value("${sso.realm}") private String ssoRealm; + + @Value("${sb.es.user.notification.preference.index}") + private String sbUserNotificationPreferenceIndex; public String getUserAssessmentSubmissionDuration() { return userAssessmentSubmissionDuration; @@ -1617,4 +1620,12 @@ public String getSsoRealm() { public void setSsoRealm(String ssoRealm) { this.ssoRealm = ssoRealm; } + + public String getSbUserNotificationPreferenceIndex() { + return sbUserNotificationPreferenceIndex; + } + + public void setSbUserNotificationPreferenceIndex(String sbUserNotificationPreferenceIndex) { + this.sbUserNotificationPreferenceIndex = sbUserNotificationPreferenceIndex; + } } \ 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 6f37733ee..db0c55c3e 100644 --- a/src/main/java/org/sunbird/common/util/Constants.java +++ b/src/main/java/org/sunbird/common/util/Constants.java @@ -569,6 +569,13 @@ public class Constants { public static final String ASSESSMENT_READ_RESPONSE = "assessmentreadresponse"; public static final String API_SUBMIT_ASSESSMENT = "api.submit.asssessment"; + + public static final String API_READ_NOTIFICATION_PREFERENCE = "api.read.notification.preference"; + public static final String API_UPDATE_NOTIFICATION_PREFERENCE = "api.update.notification.preference"; + public static final String NOTIFICATION_PREFERENCE="notification_preference"; + public static final String TABLE_USER_NOTIFICATION_PREFERENCE = "user_notification_preference"; + public static final String ERROR_INVALID_USER_ID = "Invalid UserId"; + public static final String ERROR_INVALID_REQUEST_BODY = "Invalid Request Body"; private Constants() { throw new IllegalStateException("Utility class"); diff --git a/src/main/java/org/sunbird/profile/controller/ProfileController.java b/src/main/java/org/sunbird/profile/controller/ProfileController.java index 56611e770..c70e07186 100644 --- a/src/main/java/org/sunbird/profile/controller/ProfileController.java +++ b/src/main/java/org/sunbird/profile/controller/ProfileController.java @@ -89,4 +89,17 @@ public ResponseEntity getBulkUploadDetails(@PathVariable("orgId") String orgI SBApiResponse response = profileService.getBulkUploadDetails(orgId); return new ResponseEntity<>(response, response.getResponseCode()); } + + @GetMapping("/user/v1/notificationPreference") + public ResponseEntity getNotificationPreferences(@RequestHeader(Constants.X_AUTH_USER_ID) String userId) { + SBApiResponse response = profileService.getNotificationPreferencesById(userId); + return new ResponseEntity<>(response, response.getResponseCode()); + } + + @PostMapping("/user/v1/notificationPreference") + public ResponseEntity updateNotificationPreferences(@RequestHeader(Constants.X_AUTH_USER_ID) String userId, + @RequestBody Map request) { + SBApiResponse response = profileService.updateNotificationPreference(userId,request); + return new ResponseEntity<>(response, response.getResponseCode()); + } } diff --git a/src/main/java/org/sunbird/profile/service/ProfileService.java b/src/main/java/org/sunbird/profile/service/ProfileService.java index d885c50de..afab7a58e 100644 --- a/src/main/java/org/sunbird/profile/service/ProfileService.java +++ b/src/main/java/org/sunbird/profile/service/ProfileService.java @@ -25,4 +25,9 @@ public interface ProfileService { SBApiResponse bulkUpload(MultipartFile mFile, String orgId, String orgName, String userId); SBApiResponse getBulkUploadDetails(String orgId); + + SBApiResponse getNotificationPreferencesById(String userId); + + SBApiResponse updateNotificationPreference(String userId,Map request); + } diff --git a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java index c7b2a3ccb..6df0cc0c7 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 java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,6 +42,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; @Service @SuppressWarnings({ "unchecked", "serial" }) @@ -70,6 +72,9 @@ public class ProfileServiceImpl implements ProfileService { @Autowired StorageServiceImpl storageService; + @Autowired + Gson gson; + private Logger log = LoggerFactory.getLogger(getClass().getName()); @Override @@ -1023,7 +1028,7 @@ private String executeMigrateUser(Map request, Map) migrateResponse.get(Constants.PARAMS)) - .get(Constants.ERROR_MESSAGE); + .get(Constants.ERROR_MESSAGE); } return errMsg; } @@ -1186,4 +1191,85 @@ private void sendBulkUploadNotification(String orgId, String orgName, String fil serverConfig.getSbUrl() + serverConfig.getSbSendNotificationEmailPath(), request, ProjectUtil.getDefaultHeaders()); } + + @Override + public SBApiResponse getNotificationPreferencesById(String userId) { + SBApiResponse response = ProjectUtil.createDefaultResponse(Constants.API_READ_NOTIFICATION_PREFERENCE); + String errMsg = null; + if (StringUtils.isEmpty(userId)) { + response.getParams().setErrmsg(Constants.ERROR_INVALID_USER_ID); + response.setResponseCode(HttpStatus.BAD_REQUEST); + response.getParams().setStatus(Constants.FAILED); + return response; + } + Map request = new HashMap<>(); + request.put(Constants.USER_ID, userId); + try { + List> notificationPreferences = cassandraOperation.getRecordsByProperties( + Constants.KEYSPACE_SUNBIRD, Constants.TABLE_USER_NOTIFICATION_PREFERENCE, request, + Collections.singletonList(Constants.NOTIFICATION_PREFERENCE)); + for (Map objectMap : notificationPreferences) { + Map responseMap = gson.fromJson(String.valueOf(objectMap), Map.class); + response.getResult().putAll(responseMap); + } + response.setResponseCode(HttpStatus.OK); + response.getParams().setStatus(Constants.SUCCESS); + } catch (Exception e) { + errMsg = "Failed to read user notification preference. Exception: " + e.getMessage(); + log.error(errMsg, e); + } + if (StringUtils.isNotBlank(errMsg)) { + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(errMsg); + response.setResponseCode(HttpStatus.BAD_REQUEST); + } + return response; + } + + @Override + public SBApiResponse updateNotificationPreference(String userId, Map request) { + SBApiResponse response = ProjectUtil.createDefaultResponse(Constants.API_UPDATE_NOTIFICATION_PREFERENCE); + if (StringUtils.isEmpty(userId)) { + response.getParams().setErrmsg(Constants.ERROR_INVALID_USER_ID); + response.setResponseCode(HttpStatus.BAD_REQUEST); + response.getParams().setStatus(Constants.FAILED); + return response; + } + String errMsg = null; + try { + Map requestBody = (Map) request.get(Constants.REQUEST); + if (MapUtils.isNotEmpty(requestBody)) { + Map updateRequest = new HashMap<>(); + updateRequest.put(Constants.NOTIFICATION_PREFERENCE, mapper.writeValueAsString(requestBody)); + Map key = new HashMap() { + private static final long serialVersionUID = 1L; + { + put(Constants.USER_ID, userId); + } + }; + cassandraOperation.updateRecord(Constants.KEYSPACE_SUNBIRD, + Constants.TABLE_USER_NOTIFICATION_PREFERENCE, updateRequest, key); + RestStatus restStatus = indexerService.updateEntity(serverConfig.getSbUserNotificationPreferenceIndex(), + serverConfig.getEsProfileIndexType(), userId, mapper.convertValue(requestBody, Map.class)); + if (restStatus != null && Constants.OK.equalsIgnoreCase(restStatus.name())) { + response.setResponseCode(HttpStatus.OK); + response.getParams().setStatus(Constants.SUCCESS); + } else { + response.getParams().setStatus(Constants.FAILED); + errMsg = "Failed to update Notification Preference Index"; + } + } else { + errMsg = Constants.ERROR_INVALID_REQUEST_BODY; + } + } catch (Exception e) { + errMsg = "Failed to update notification preference records. Exception: " + e.getMessage(); + log.error(errMsg, e); + } + if (StringUtils.isNotBlank(errMsg)) { + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(errMsg); + response.setResponseCode(HttpStatus.BAD_REQUEST); + } + return response; + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index aba381b73..754b76905 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -54,6 +54,7 @@ sb.es.port=9200 sb.es.username= sb.es.password= sb.es.user.profile.index=user_alias +sb.es.user.notification.preference.index=notify_preference es.profile.index=userprofile es.profile.index.type=_doc From 7595ab4704461173e9e9e20d0643f52ae3c52a88 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 18 Nov 2022 17:16:55 +0530 Subject: [PATCH 05/13] Fixes for user notification preference --- .../sunbird/common/util/IndexerService.java | 19 +++++++-- .../profile/service/ProfileServiceImpl.java | 41 +++++++++++++++---- .../service/AllocationService.java | 19 +++++---- .../service/AllocationServiceV2.java | 2 +- 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/sunbird/common/util/IndexerService.java b/src/main/java/org/sunbird/common/util/IndexerService.java index 8770d3337..6cd3924d3 100644 --- a/src/main/java/org/sunbird/common/util/IndexerService.java +++ b/src/main/java/org/sunbird/common/util/IndexerService.java @@ -48,7 +48,11 @@ public class IndexerService { * @param indexDocument index Document * @return status */ - public RestStatus addEntity(String index, String indexType, String entityId, Map indexDocument) { + public RestStatus addEntity(String index, String indexType, String entityId, Map indexDocument) throws Exception { + return addEntity(index, indexType, entityId, indexDocument, false); + } + + public RestStatus addEntity(String index, String indexType, String entityId, Map indexDocument, boolean isSunbirdES) throws Exception { logger.info("addEntity starts with index {} and entityId {}", index, entityId); IndexResponse response = null; try { @@ -59,6 +63,7 @@ public RestStatus addEntity(String index, String indexType, String entityId, Map } } catch (IOException e) { logger.error("Exception in adding record to ElasticSearch", e); + throw e; } if (null == response) return null; @@ -72,11 +77,19 @@ public RestStatus addEntity(String index, String indexType, String entityId, Map * @param indexDocument index Document * @return status */ - public RestStatus updateEntity(String index, String indexType, String entityId, Map indexDocument) { + public RestStatus updateEntity(String index, String indexType, String entityId, Map indexDocument) { + return updateEntity(index, indexType, entityId, indexDocument, false); + } + + public RestStatus updateEntity(String index, String indexType, String entityId, Map indexDocument, boolean isSunbirdES) { logger.info("updateEntity starts with index {} and entityId {}", index, entityId); UpdateResponse response = null; try { - response = esClient.update(new UpdateRequest(index.toLowerCase(), indexType, entityId).doc(indexDocument), RequestOptions.DEFAULT); + if(isSunbirdES) { + response = sbEsClient.update(new UpdateRequest(index.toLowerCase(), indexType, entityId).doc(indexDocument), RequestOptions.DEFAULT); + } else { + response = esClient.update(new UpdateRequest(index.toLowerCase(), indexType, entityId).doc(indexDocument), RequestOptions.DEFAULT); + } } catch (IOException e) { logger.error("Exception in updating a record to ElasticSearch", e); } diff --git a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java index 6df0cc0c7..8876fd3c2 100644 --- a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java +++ b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java @@ -1239,6 +1239,7 @@ public SBApiResponse updateNotificationPreference(String userId, Map requestBody = (Map) request.get(Constants.REQUEST); if (MapUtils.isNotEmpty(requestBody)) { + requestBody.put(Constants.USER_ID, userId); Map updateRequest = new HashMap<>(); updateRequest.put(Constants.NOTIFICATION_PREFERENCE, mapper.writeValueAsString(requestBody)); Map key = new HashMap() { @@ -1249,14 +1250,21 @@ public SBApiResponse updateNotificationPreference(String userId, Map existingData = getRegistrationDoc(key); + + RestStatus restStatus = null; + if (existingData == null) { + restStatus = indexerService.addEntity(serverConfig.getSbUserNotificationPreferenceIndex(), + serverConfig.getEsProfileIndexType(), userId, requestBody, true); } else { - response.getParams().setStatus(Constants.FAILED); - errMsg = "Failed to update Notification Preference Index"; + restStatus = indexerService.updateEntity(serverConfig.getSbUserNotificationPreferenceIndex(), + serverConfig.getEsProfileIndexType(), userId, mapper.convertValue(requestBody, Map.class), + true); + if (restStatus == null || !Constants.OK.equalsIgnoreCase(restStatus.name())) { + response.getParams().setStatus(Constants.FAILED); + errMsg = "Failed to update Notification Preference Index"; + } } } else { errMsg = Constants.ERROR_INVALID_REQUEST_BODY; @@ -1272,4 +1280,23 @@ public SBApiResponse updateNotificationPreference(String userId, Map getRegistrationDoc(Map key) throws Exception { + SearchResponse searchResponse = indexerService.getEsResult(serverConfig.getSbUserNotificationPreferenceIndex(), + serverConfig.getEsProfileIndexType(), queryBuilder(key), false); + + if (searchResponse.getHits().getTotalHits() > 0) { + SearchHit hit = searchResponse.getHits().getAt(0); + return mapper.convertValue(hit.getSourceAsMap(), Map.class); + } + return null; + } + + private SearchSourceBuilder queryBuilder(Map mustMatch) { + BoolQueryBuilder boolBuilder = new BoolQueryBuilder(); + for (Map.Entry entry : mustMatch.entrySet()) { + boolBuilder.must(QueryBuilders.termQuery(entry.getKey() + ".raw", entry.getValue())); + } + return new SearchSourceBuilder().query(boolBuilder); + } } diff --git a/src/main/java/org/sunbird/workallocation/service/AllocationService.java b/src/main/java/org/sunbird/workallocation/service/AllocationService.java index 539972bad..a3ce10fb0 100644 --- a/src/main/java/org/sunbird/workallocation/service/AllocationService.java +++ b/src/main/java/org/sunbird/workallocation/service/AllocationService.java @@ -141,15 +141,20 @@ public Response addWorkAllocation(String userAuthToken, String userId, WorkAlloc workAllocation.setActiveWAObject(null); workAllocation.setArchivedWAList(null); } - RestStatus restStatus = indexerService.addEntity(index, indexType, workAllocationDTO.getUserId(), - mapper.convertValue(workAllocation, Map.class)); + RestStatus restStatus; Response response = new Response(); - if (!ObjectUtils.isEmpty(restStatus)) { - response.put(Constants.MESSAGE, Constants.SUCCESSFUL); - } else { - response.put(Constants.MESSAGE, Constants.FAILED); + try { + restStatus = indexerService.addEntity(index, indexType, workAllocationDTO.getUserId(), + mapper.convertValue(workAllocation, Map.class)); + if (!ObjectUtils.isEmpty(restStatus)) { + response.put(Constants.MESSAGE, Constants.SUCCESSFUL); + response.put(Constants.DATA, restStatus); + } else { + response.put(Constants.MESSAGE, Constants.FAILED); + } + } catch (Exception e) { + logger.error("Failed to add workallocation into ES. Exception: ", e); } - response.put(Constants.DATA, restStatus); response.put(Constants.STATUS, HttpStatus.OK); return response; } diff --git a/src/main/java/org/sunbird/workallocation/service/AllocationServiceV2.java b/src/main/java/org/sunbird/workallocation/service/AllocationServiceV2.java index 4dd05e4e1..e999a1ad9 100644 --- a/src/main/java/org/sunbird/workallocation/service/AllocationServiceV2.java +++ b/src/main/java/org/sunbird/workallocation/service/AllocationServiceV2.java @@ -539,7 +539,7 @@ public Response copyWorkOrder(String userId, WorkOrderDTO workOrderDTO) { restStatus = indexerService.addEntity(workOrderIndex, workOrderIndexType, workOrder.getId(), mapper.convertValue(workOrder, Map.class)); - } catch (JsonProcessingException e) { + } catch (Exception e) { logger.error("Exception occurred while saving the work order!!", e); throw new ApplicationLogicError("Exception occurred while saving the work order!!", e); } From 8be9b172c6933f9ae9ff31815c19947e63dfc53f Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 18 Nov 2022 17:54:27 +0530 Subject: [PATCH 06/13] Updated search index for notificationPreference. --- .../org/sunbird/profile/service/ProfileServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java index 8876fd3c2..002e2e273 100644 --- a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java +++ b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java @@ -1251,7 +1251,7 @@ public SBApiResponse updateNotificationPreference(String userId, Map existingData = getRegistrationDoc(key); + Map existingData = getPreferenceDoc(key); RestStatus restStatus = null; if (existingData == null) { @@ -1281,9 +1281,9 @@ public SBApiResponse updateNotificationPreference(String userId, Map getRegistrationDoc(Map key) throws Exception { + private Map getPreferenceDoc(Map key) throws Exception { SearchResponse searchResponse = indexerService.getEsResult(serverConfig.getSbUserNotificationPreferenceIndex(), - serverConfig.getEsProfileIndexType(), queryBuilder(key), false); + serverConfig.getEsProfileIndexType(), queryBuilder(key), true); if (searchResponse.getHits().getTotalHits() > 0) { SearchHit hit = searchResponse.getHits().getAt(0); From 5eec6a22f245720a56e5d4ce534b34f66bb6316e Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 18 Nov 2022 19:48:10 +0530 Subject: [PATCH 07/13] Fixes for ES add and read functionalities --- .../sunbird/common/util/IndexerService.java | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/sunbird/common/util/IndexerService.java b/src/main/java/org/sunbird/common/util/IndexerService.java index 6cd3924d3..4e697e4ba 100644 --- a/src/main/java/org/sunbird/common/util/IndexerService.java +++ b/src/main/java/org/sunbird/common/util/IndexerService.java @@ -55,13 +55,19 @@ public RestStatus addEntity(String index, String indexType, String entityId, Map public RestStatus addEntity(String index, String indexType, String entityId, Map indexDocument, boolean isSunbirdES) throws Exception { logger.info("addEntity starts with index {} and entityId {}", index, entityId); IndexResponse response = null; - try { - if(!StringUtils.isEmpty(entityId)){ - response = esClient.index(new IndexRequest(index, indexType, entityId).source(indexDocument), RequestOptions.DEFAULT); - }else{ - response = esClient.index(new IndexRequest(index, indexType).source(indexDocument), RequestOptions.DEFAULT); - } - } catch (IOException e) { + try { + IndexRequest indexRequest = null; + if (!StringUtils.isEmpty(entityId)) { + indexRequest = new IndexRequest(index, indexType, entityId); + } else { + indexRequest = new IndexRequest(index, indexType); + } + if (isSunbirdES) { + response = sbEsClient.index(indexRequest.source(indexDocument), RequestOptions.DEFAULT); + } else { + response = esClient.index(indexRequest.source(indexDocument), RequestOptions.DEFAULT); + } + } catch (IOException e) { logger.error("Exception in adding record to ElasticSearch", e); throw e; } @@ -104,18 +110,26 @@ public RestStatus updateEntity(String index, String indexType, String entityId, * @param entityId entity Id * @return status */ - public Map readEntity(String index, String indexType, String entityId){ - logger.info("readEntity starts with index {} and entityId {}", index, entityId); - GetResponse response = null; - try { - response = esClient.get(new GetRequest(index, indexType, entityId), RequestOptions.DEFAULT); - } catch (IOException e) { - logger.error("Exception in getting the record from ElasticSearch", e); - } - if(null == response) - return null; - return response.getSourceAsMap(); - } + public Map readEntity(String index, String indexType, String entityId) { + return readEntity(index, indexType, entityId, false); + } + + public Map readEntity(String index, String indexType, String entityId, boolean isSunbirdES) { + logger.info("readEntity starts with index {} and entityId {}", index, entityId); + GetResponse response = null; + try { + if (isSunbirdES) { + response = sbEsClient.get(new GetRequest(index, indexType, entityId), RequestOptions.DEFAULT); + } else { + response = esClient.get(new GetRequest(index, indexType, entityId), RequestOptions.DEFAULT); + } + } catch (IOException e) { + logger.error("Exception in getting the record from ElasticSearch", e); + } + if (null == response) + return null; + return response.getSourceAsMap(); + } /** * Search the document in es based on provided information From 714e9ed8ffdaf79c4f312f58737abf48eb53507f Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Mon, 21 Nov 2022 15:53:13 +0530 Subject: [PATCH 08/13] Using proper error code for user patch API --- src/main/java/org/sunbird/common/util/Constants.java | 1 + .../org/sunbird/profile/service/ProfileServiceImpl.java | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sunbird/common/util/Constants.java b/src/main/java/org/sunbird/common/util/Constants.java index db0c55c3e..2432da60b 100644 --- a/src/main/java/org/sunbird/common/util/Constants.java +++ b/src/main/java/org/sunbird/common/util/Constants.java @@ -576,6 +576,7 @@ public class Constants { public static final String TABLE_USER_NOTIFICATION_PREFERENCE = "user_notification_preference"; public static final String ERROR_INVALID_USER_ID = "Invalid UserId"; public static final String ERROR_INVALID_REQUEST_BODY = "Invalid Request Body"; + public static final String CLIENT_ERROR = "CLIENT_ERROR"; private Constants() { throw new IllegalStateException("Utility class"); diff --git a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java index 002e2e273..724934900 100644 --- a/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java +++ b/src/main/java/org/sunbird/profile/service/ProfileServiceImpl.java @@ -151,12 +151,17 @@ public SBApiResponse profileUpdate(Map request, String userToken url.append(serverConfig.getSbUrl()).append(serverConfig.getLmsUserUpdatePath()); updateResponse = outboundRequestHandlerService.fetchResultUsingPatch( serverConfig.getSbUrl() + serverConfig.getLmsUserUpdatePath(), updateRequest, headerValues); - if (updateResponse.get(Constants.RESPONSE_CODE).equals(Constants.OK)) { + if (Constants.OK.equalsIgnoreCase((String) updateResponse.get(Constants.RESPONSE_CODE))) { response.setResponseCode(HttpStatus.OK); response.getResult().put(Constants.RESPONSE, Constants.SUCCESS); response.getParams().setStatus(Constants.SUCCESS); } else { - response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); + if (updateResponse != null && Constants.CLIENT_ERROR + .equalsIgnoreCase((String) updateResponse.get(Constants.RESPONSE_CODE))) { + response.setResponseCode(HttpStatus.BAD_REQUEST); + } else { + response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); + } response.getParams().setStatus(Constants.FAILED); String errMsg = (String) ((Map) updateResponse.get(Constants.PARAMS)) .get(Constants.ERROR_MESSAGE); From cdcf60f082445ab2bee1f623bda19ac06c9df302 Mon Sep 17 00:00:00 2001 From: Juhi Date: Tue, 22 Nov 2022 12:36:56 +0530 Subject: [PATCH 09/13] final changes --- .../service/AssessmentServiceV2Impl.java | 1258 ++++++++--------- src/main/resources/application.properties | 14 +- 2 files changed, 635 insertions(+), 637 deletions(-) diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java index f54903ea7..2bde14ab1 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java @@ -39,634 +39,632 @@ @SuppressWarnings("unchecked") public class AssessmentServiceV2Impl implements AssessmentServiceV2 { - private final Logger logger = LoggerFactory.getLogger(AssessmentServiceV2Impl.class); - - @Autowired - AssessmentUtilServiceV2 assessUtilServ; - - @Autowired - CbExtServerProperties serverProperties; - - @Autowired - Producer kafkaProducer; - - @Autowired - AssessmentRepository assessmentRepository; - - @Autowired - RequestInterceptor requestInterceptor; - - public SBApiResponse readAssessment(String assessmentIdentifier, String token) { - logger.info("AssessmentServiceV2Impl::readAssessment... Started"); - SBApiResponse response = createDefaultResponse(Constants.API_QUESTIONSET_HIERARCHY_GET); - String errMsg; - try { - String userId = validateAuthTokenAndFetchUserId(token); - if (userId != null) { - logger.info("readAssessment.. userId :" + userId); - Map assessmentAllDetail = new HashMap<>(); - errMsg = fetchReadHierarchyDetails(assessmentAllDetail, token, assessmentIdentifier); - if (errMsg.isEmpty() && !((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) - .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { - logger.info("Fetched assessment Details... for : " + assessmentIdentifier); - List> existingDataList = assessmentRepository - .fetchUserAssessmentDataFromDB(userId, assessmentIdentifier); - Timestamp assessmentStartTime = new Timestamp(new Date().getTime()); - if (existingDataList.isEmpty()) { - logger.info("Assessment read first time for user."); - response.getResult().put(Constants.QUESTION_SET, readAssessmentLevelData(assessmentAllDetail)); - int expectedDuration = (Integer) assessmentAllDetail.get(Constants.EXPECTED_DURATION); - Boolean isAssessmentUpdatedToDB = assessmentRepository.addUserAssesmentDataToDB(userId, - assessmentIdentifier, assessmentStartTime, - calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime), - (Map) (response.getResult().get(Constants.QUESTION_SET)), - Constants.NOT_SUBMITTED); - if (Boolean.FALSE.equals(isAssessmentUpdatedToDB)) { - errMsg = Constants.ASSESSMENT_DATA_START_TIME_NOT_UPDATED; - } - } else { - logger.info("Assessment read... user has details... "); - Date existingAssessmentEndTime = (Date) (existingDataList.get(0).get(Constants.END_TIME)); - int time = assessmentStartTime.compareTo(existingAssessmentEndTime); - if (time < 0 && ((String) existingDataList.get(0).get(Constants.STATUS)) - .equalsIgnoreCase(Constants.NOT_SUBMITTED)) { - String questionSetFromAssessmentString = (String) existingDataList.get(0) - .get(Constants.ASSESSMENT_READ_RESPONSE); - Map questionSetFromAssessment = new Gson().fromJson( - questionSetFromAssessmentString, new TypeToken>() { - }.getType()); - response.getResult().put(Constants.QUESTION_SET, questionSetFromAssessment); - } else { - logger.info("Assessment read... adding user data to db..."); - response.getResult().put(Constants.QUESTION_SET, - readAssessmentLevelData(assessmentAllDetail)); - int expectedDuration = (Integer) assessmentAllDetail.get(Constants.EXPECTED_DURATION); - Boolean isAssessmentUpdatedToDB = assessmentRepository.addUserAssesmentDataToDB(userId, - assessmentIdentifier, assessmentStartTime, - calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime), - (Map) (response.getResult().get(Constants.QUESTION_SET)), - Constants.NOT_SUBMITTED); - if (Boolean.FALSE.equals(isAssessmentUpdatedToDB)) { - errMsg = Constants.ASSESSMENT_DATA_START_TIME_NOT_UPDATED; - } - } - } - } else if (errMsg.isEmpty() && ((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) - .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { - response.getResult().put(Constants.QUESTION_SET, readAssessmentLevelData(assessmentAllDetail)); - } - } else { - errMsg = Constants.USER_ID_DOESNT_EXIST; - } - } catch (Exception e) { - logger.error(String.format("Exception in %s : %s", "read Assessment", e.getMessage()), e); - errMsg = "Failed to read Assessment. Exception: " + e.getMessage(); - } - if (StringUtils.isNotBlank(errMsg)) { - response.getParams().setStatus(Constants.FAILED); - response.getParams().setErrmsg(errMsg); - response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); - } - return response; - } - - public SBApiResponse readQuestionList(Map requestBody, String authUserToken) { - SBApiResponse response = createDefaultResponse(Constants.API_SUBMIT_ASSESSMENT); - String errMsg; - String primaryCategory = ""; - Map result = new HashMap<>(); - try { - List identifierList = new ArrayList<>(); - List questionList = new ArrayList<>(); - result = validateQuestionListAPI(requestBody, authUserToken, identifierList); - errMsg = result.get(Constants.ERROR_MESSAGE); - if (errMsg.isEmpty()) { - if(result.containsKey(Constants.PRIMARY_CATEGORY) && result.get(Constants.PRIMARY_CATEGORY).equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) - primaryCategory = result.get(Constants.PRIMARY_CATEGORY); - errMsg = assessUtilServ.fetchQuestionIdentifierValue(identifierList, questionList, primaryCategory); - if (errMsg.isEmpty() && identifierList.size() == questionList.size()) { - response.getResult().put(Constants.QUESTIONS, questionList); - } - } - } catch (Exception e) { - logger.error(String.format("Exception in %s : %s", "get Question List", e.getMessage()), e); - errMsg = "Failed to fetch the question list. Exception: " + e.getMessage(); - } - if (StringUtils.isNotBlank(errMsg)) { - response.getParams().setStatus(Constants.FAILED); - response.getParams().setErrmsg(errMsg); - response.setResponseCode(HttpStatus.BAD_REQUEST); - } - return response; - - } - - private String validateAuthTokenAndFetchUserId(String authUserToken) { - return requestInterceptor.fetchUserIdFromAccessToken(authUserToken); - } - - private String fetchReadHierarchyDetails(Map assessmentAllDetail, String token, - String assessmentIdentifier) { - Map readHierarchyApiResponse = assessUtilServ.getReadHierarchyApiResponse(assessmentIdentifier, - token); - if (readHierarchyApiResponse.isEmpty() - || !Constants.OK.equalsIgnoreCase((String) readHierarchyApiResponse.get(Constants.RESPONSE_CODE))) { - return Constants.ASSESSMENT_HIERARCHY_READ_FAILED; - } - assessmentAllDetail - .putAll((Map) ((Map) readHierarchyApiResponse.get(Constants.RESULT)) - .get(Constants.QUESTION_SET)); - - return StringUtils.EMPTY; - } - - private Map validateQuestionListAPI(Map requestBody, String authUserToken, - List identifierList) { - Map result = new HashMap<>(); - String userId = validateAuthTokenAndFetchUserId(authUserToken); - if (StringUtils.isBlank(userId)) { - result.put(Constants.ERROR_MESSAGE, Constants.USER_ID_DOESNT_EXIST); - return result; - } - - if (StringUtils.isBlank((String) requestBody.get(Constants.ASSESSMENT_ID_KEY))) { - result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_ID_KEY_IS_NOT_PRESENT_IS_EMPTY); - return result; - } - - identifierList.addAll(getQuestionIdList(requestBody)); - if (identifierList.isEmpty()) { - result.put(Constants.ERROR_MESSAGE, Constants.IDENTIFIER_LIST_IS_EMPTY); - return result; - } - - Map assessmentDetail = new HashMap<>(); - fetchReadHierarchyDetails(assessmentDetail, authUserToken, - (String) requestBody.get(Constants.ASSESSMENT_ID_KEY)); - - if (ObjectUtils.isEmpty(assessmentDetail)) { - result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_HIERARCHY_READ_FAILED); - return result; - } - - if (!((String) assessmentDetail.get(Constants.PRIMARY_CATEGORY)) - .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { - List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, - (String) requestBody.get(Constants.ASSESSMENT_ID_KEY)); - String questionSetFromAssessmentString = (!existingDataList.isEmpty()) - ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) - : ""; - if (!questionSetFromAssessmentString.isEmpty()) { - Map questionSetFromAssessment = new Gson().fromJson(questionSetFromAssessmentString, - new TypeToken>() { - }.getType()); - List questionsFromAssessment = new ArrayList<>(); - List> sections = (List>) questionSetFromAssessment - .get(Constants.CHILDREN); - for (Map section : sections) { - questionsFromAssessment.addAll((List) section.get(Constants.CHILD_NODES)); - } - // Out of the list of questions received in the payload, checking if the request - // has only those ids which are a part of the user's latest assessment - // Fetching all the remaining questions details from the Redis - if (Boolean.FALSE.equals(validateQuestionListRequest(identifierList, questionsFromAssessment))) { - result.put(Constants.ERROR_MESSAGE, Constants.THE_QUESTIONS_IDS_PROVIDED_DONT_MATCH); - return result; - } - } else { - result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_ID_INVALID_SESSION_EXPIRED); - return result; - } - } - else - { - result.put(Constants.PRIMARY_CATEGORY, Constants.PRACTICE_QUESTION_SET); - } - result.put(Constants.ERROR_MESSAGE, ""); - return result; - } - - @Override - public SBApiResponse submitAssessment(Map submitRequest, String authUserToken) { - SBApiResponse outgoingResponse = createDefaultResponse(Constants.API_SUBMIT_ASSESSMENT); - String errMsg; - List> sectionListFromSubmitRequest = new ArrayList<>(); - List> hierarchySectionList = new ArrayList<>(); - Map allHierarchy = new HashMap<>(); - List questionsListFromAssessmentHierarchy = new ArrayList<>(); - errMsg = validateSubmitAssessmentRequest(submitRequest, authUserToken, hierarchySectionList, - sectionListFromSubmitRequest, allHierarchy); - if (errMsg.isEmpty()) { - String userId = validateAuthTokenAndFetchUserId(authUserToken); - String scoreCutOffType = ((String) allHierarchy.get(Constants.SCORE_CUTOFF_TYPE)).toLowerCase(); - List> existingDataList = new ArrayList<>(); - List> sectionLevelsResults = new ArrayList<>(); - for (Map hierarchySection : hierarchySectionList) { - String hierarchySectionId = (String) hierarchySection.get(Constants.IDENTIFIER); - String userSectionId = ""; - Map userSectionData = new HashMap<>(); - for (Map sectionFromSubmitRequest : sectionListFromSubmitRequest) { - userSectionId = (String) sectionFromSubmitRequest.get(Constants.IDENTIFIER); - if (userSectionId.equalsIgnoreCase(hierarchySectionId)) { - userSectionData = sectionFromSubmitRequest; - break; - } - } - if (!((String) (allHierarchy.get(Constants.PRIMARY_CATEGORY))) - .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { - existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, - (String) submitRequest.get(Constants.IDENTIFIER)); - String questionSetFromAssessmentString = (!existingDataList.isEmpty()) - ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) - : ""; - if (!questionSetFromAssessmentString.isEmpty()) { - Map questionSetFromAssessment = new Gson() - .fromJson(questionSetFromAssessmentString, new TypeToken>() { - }.getType()); - if (questionSetFromAssessment != null - && questionSetFromAssessment.get(Constants.CHILDREN) != null) { - List> sections = (List>) questionSetFromAssessment - .get(Constants.CHILDREN); - for (Map section : sections) { - String sectionId = (String) section.get(Constants.IDENTIFIER); - if (userSectionId.equalsIgnoreCase(sectionId)) { - questionsListFromAssessmentHierarchy = (List) section - .get(Constants.CHILD_NODES); - break; - } - } - } else { - errMsg = "Question Set From The Database returns Null"; - outgoingResponse.getResult().clear(); - break; - } - - hierarchySection.put(Constants.SCORE_CUTOFF_TYPE, scoreCutOffType); - List> questionsListFromSubmitRequest = new ArrayList<>(); - if (userSectionData.containsKey(Constants.CHILDREN) - && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { - questionsListFromSubmitRequest = (List>) userSectionData - .get(Constants.CHILDREN); - } - Map result = new HashMap<>(); - switch (scoreCutOffType) { - case Constants.ASSESSMENT_LEVEL_SCORE_CUTOFF: { - result.putAll(createResponseMapWithProperStructure(hierarchySection, - assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, - questionsListFromSubmitRequest))); - outgoingResponse.getResult().putAll(calculateAssessmentFinalResults(result)); - writeDataToDatabaseAndTriggerKafkaEvent(submitRequest, userId, existingDataList, result, - (String) allHierarchy.get(Constants.PRIMARY_CATEGORY)); - return outgoingResponse; - } - case Constants.SECTION_LEVEL_SCORE_CUTOFF: { - result.putAll(createResponseMapWithProperStructure(hierarchySection, - assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, - questionsListFromSubmitRequest))); - sectionLevelsResults.add(result); - } - break; - default: - break; - } - } - } else { - hierarchySection.put(Constants.SCORE_CUTOFF_TYPE, scoreCutOffType); - List> questionsListFromSubmitRequest = new ArrayList<>(); - if (userSectionData.containsKey(Constants.CHILDREN) - && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { - questionsListFromSubmitRequest = (List>) userSectionData - .get(Constants.CHILDREN); - } - List desiredKeys = Lists.newArrayList(Constants.IDENTIFIER); - List questionsList = questionsListFromSubmitRequest.stream() - .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); - questionsListFromAssessmentHierarchy = questionsList.stream() - .map(object -> Objects.toString(object, null)).collect(Collectors.toList()); - Map result = new HashMap<>(); - switch (scoreCutOffType) { - case Constants.ASSESSMENT_LEVEL_SCORE_CUTOFF: { - result.putAll(createResponseMapWithProperStructure(hierarchySection, - assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, - questionsListFromSubmitRequest))); - outgoingResponse.getResult().putAll(calculateAssessmentFinalResults(result)); - return outgoingResponse; - } - case Constants.SECTION_LEVEL_SCORE_CUTOFF: { - result.putAll(createResponseMapWithProperStructure(hierarchySection, - assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, - questionsListFromSubmitRequest))); - sectionLevelsResults.add(result); - } - break; - default: - break; - } - } - } - if (errMsg.isEmpty() && !ObjectUtils.isEmpty(scoreCutOffType) - && scoreCutOffType.equalsIgnoreCase(Constants.SECTION_LEVEL_SCORE_CUTOFF)) { - Map result = calculateSectionFinalResults(sectionLevelsResults); - outgoingResponse.getResult().putAll(result); - writeDataToDatabaseAndTriggerKafkaEvent(submitRequest, userId, existingDataList, result, - (String) allHierarchy.get(Constants.PRIMARY_CATEGORY)); - return outgoingResponse; - } - } - if (StringUtils.isNotBlank(errMsg)) { - outgoingResponse.getParams().setStatus(Constants.FAILED); - outgoingResponse.getParams().setErrmsg(errMsg); - outgoingResponse.setResponseCode(HttpStatus.BAD_REQUEST); - } - return outgoingResponse; - } - - private void writeDataToDatabaseAndTriggerKafkaEvent(Map submitRequest, String userId, - List> existingDataList, Map result, String primaryCategory) { - Date startTime = (!existingDataList.isEmpty()) ? (Date) existingDataList.get(0).get(Constants.START_TIME) - : null; - Boolean isAssessmentUpdatedToDB = assessmentRepository.updateUserAssesmentDataToDB(userId, - (String) submitRequest.get(Constants.IDENTIFIER), submitRequest, result, Constants.SUBMITTED, - startTime); - if (Boolean.TRUE.equals(isAssessmentUpdatedToDB)) { - Map kafkaResult = new HashMap<>(); - kafkaResult.put(Constants.CONTENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); - kafkaResult.put(Constants.COURSE_ID, submitRequest.get(Constants.COURSE_ID)); - kafkaResult.put(Constants.BATCH_ID, submitRequest.get(Constants.BATCH_ID)); - kafkaResult.put(Constants.USER_ID, submitRequest.get(Constants.USER_ID)); - kafkaResult.put(Constants.ASSESSMENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); - kafkaResult.put(Constants.PRIMARY_CATEGORY, primaryCategory); - kafkaProducer.push(serverProperties.getAssessmentSubmitTopic(), kafkaResult); - } - } - - private String validateSubmitAssessmentRequest(Map submitRequest, String authUserToken, - List> hierarchySectionList, List> sectionListFromSubmitRequest, - Map assessmentHierarchy) { - String userId = validateAuthTokenAndFetchUserId(authUserToken); - if (ObjectUtils.isEmpty(userId)) { - return Constants.USER_ID_DOESNT_EXIST; - } - submitRequest.put(Constants.USER_ID, userId); - if (StringUtils.isEmpty((String) submitRequest.get(Constants.IDENTIFIER))) { - return Constants.INVALID_ASSESSMENT_ID; - } - String assessmentIdFromRequest = (String) submitRequest.get(Constants.IDENTIFIER); - String errMsg = fetchReadHierarchyDetails(assessmentHierarchy, authUserToken, assessmentIdFromRequest); - if (!errMsg.isEmpty()) { - return errMsg; - } - if (ObjectUtils.isEmpty(assessmentHierarchy)) { - return Constants.READ_ASSESSMENT_FAILED; - } - hierarchySectionList.addAll((List>) assessmentHierarchy.get(Constants.CHILDREN)); - sectionListFromSubmitRequest.addAll((List>) submitRequest.get(Constants.CHILDREN)); - if (((String) (assessmentHierarchy.get(Constants.PRIMARY_CATEGORY))) - .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) - return ""; - List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, - assessmentIdFromRequest); - Date assessmentStartTime = (!existingDataList.isEmpty()) - ? (Date) existingDataList.get(0).get(Constants.START_TIME) - : null; - if (assessmentStartTime == null) { - return Constants.READ_ASSESSMENT_START_TIME_FAILED; - } - int expectedDuration = (Integer) assessmentHierarchy.get(Constants.EXPECTED_DURATION); - Timestamp later = calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime); - Timestamp submissionTime = new Timestamp(new Date().getTime()); - int time = submissionTime.compareTo(later); - if (time <= 0) { - List desiredKeys = Lists.newArrayList(Constants.IDENTIFIER); - List hierarchySectionIds = hierarchySectionList.stream() - .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); - List submitSectionIds = sectionListFromSubmitRequest.stream() - .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); - if (!new HashSet<>(hierarchySectionIds).containsAll(submitSectionIds)) { - return Constants.WRONG_SECTION_DETAILS; - } else { - String areQuestionIdsSame = validateIfQuestionIdsAreSame(submitRequest, sectionListFromSubmitRequest, - desiredKeys, userId); - if (!areQuestionIdsSame.isEmpty()) - return areQuestionIdsSame; - } - } else { - return Constants.ASSESSMENT_SUBMIT_EXPIRED; - } - return ""; - } - - private String validateIfQuestionIdsAreSame(Map submitRequest, - List> sectionListFromSubmitRequest, List desiredKeys, String userId) { - List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, - (String) submitRequest.get(Constants.IDENTIFIER)); - String questionSetFromAssessmentString = (!existingDataList.isEmpty()) - ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) - : ""; - if (!questionSetFromAssessmentString.isEmpty()) { - Map questionSetFromAssessment = new Gson().fromJson(questionSetFromAssessmentString, - new TypeToken>() { - }.getType()); - if (questionSetFromAssessment != null && questionSetFromAssessment.get(Constants.CHILDREN) != null) { - List> sections = (List>) questionSetFromAssessment - .get(Constants.CHILDREN); - List desiredKey = Lists.newArrayList(Constants.CHILD_NODES); - List questionList = sections.stream() - .flatMap(x -> desiredKey.stream().filter(x::containsKey).map(x::get)).collect(toList()); - List questionIdsFromAssessmentHierarchy = new ArrayList<>(); - List> questionsListFromSubmitRequest = new ArrayList<>(); - for (Object question : questionList) { - questionIdsFromAssessmentHierarchy.addAll((List) question); - } - for (Map userSectionData : sectionListFromSubmitRequest) { - if (userSectionData.containsKey(Constants.CHILDREN) - && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { - questionsListFromSubmitRequest - .addAll((List>) userSectionData.get(Constants.CHILDREN)); - } - } - List userQuestionIdsFromSubmitRequest = questionsListFromSubmitRequest.stream() - .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)) - .collect(Collectors.toList()); - if (!new HashSet<>(questionIdsFromAssessmentHierarchy).containsAll(userQuestionIdsFromSubmitRequest)) { - return Constants.ASSESSMENT_SUBMIT_INVALID_QUESTION; - } - } - } else { - return Constants.ASSESSMENT_SUBMIT_QUESTION_READ_FAILED; - } - return ""; - } - - private Timestamp calculateAssessmentSubmitTime(int expectedDuration, Date assessmentStartTime) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(new Timestamp(assessmentStartTime.getTime()).getTime()); - if (serverProperties.getUserAssessmentSubmissionDuration().isEmpty()) { - serverProperties.setUserAssessmentSubmissionDuration("120"); - } - cal.add(Calendar.SECOND, - expectedDuration + Integer.parseInt(serverProperties.getUserAssessmentSubmissionDuration())); - return new Timestamp(cal.getTime().getTime()); - } - - private Map calculateAssessmentFinalResults(Map assessmentLevelResult) { - Map res = new HashMap<>(); - try { - res.put(Constants.CHILDREN, Collections.singletonList(assessmentLevelResult)); - Double result = (Double) assessmentLevelResult.get(Constants.RESULT); - res.put(Constants.OVERALL_RESULT, result); - res.put(Constants.TOTAL, assessmentLevelResult.get(Constants.TOTAL)); - res.put(Constants.BLANK, assessmentLevelResult.get(Constants.BLANK)); - res.put(Constants.CORRECT, assessmentLevelResult.get(Constants.CORRECT)); - res.put(Constants.PASS_PERCENTAGE, assessmentLevelResult.get(Constants.PASS_PERCENTAGE)); - res.put(Constants.INCORRECT, assessmentLevelResult.get(Constants.INCORRECT)); - Integer minimumPassPercentage = (Integer) assessmentLevelResult.get(Constants.PASS_PERCENTAGE); - res.put(Constants.PASS, result >= minimumPassPercentage); - } catch (Exception e) { - logger.info(e.getMessage()); - } - return res; - } - - private Map calculateSectionFinalResults(List> sectionLevelResults) { - Map res = new HashMap<>(); - Double result; - Integer correct = 0; - Integer blank = 0; - Integer inCorrect = 0; - Integer total = 0; - int pass = 0; - Double totalResult = 0.0; - try { - for (Map sectionChildren : sectionLevelResults) { - res.put(Constants.CHILDREN, sectionLevelResults); - result = (Double) sectionChildren.get(Constants.RESULT); - totalResult += result; - total += (Integer) sectionChildren.get(Constants.TOTAL); - blank += (Integer) sectionChildren.get(Constants.BLANK); - correct += (Integer) sectionChildren.get(Constants.CORRECT); - inCorrect += (Integer) sectionChildren.get(Constants.INCORRECT); - Integer minimumPassPercentage = (Integer) sectionChildren.get(Constants.PASS_PERCENTAGE); - if (result >= minimumPassPercentage) { - pass++; - } - } - res.put(Constants.OVERALL_RESULT, totalResult / sectionLevelResults.size()); - res.put(Constants.TOTAL, total); - res.put(Constants.BLANK, blank); - res.put(Constants.CORRECT, correct); - res.put(Constants.INCORRECT, inCorrect); - res.put(Constants.PASS, (pass == sectionLevelResults.size())); - } catch (Exception e) { - logger.info(e.getMessage()); - } - return res; - } - - private Map readAssessmentLevelData(Map assessmentAllDetail) { - List assessmentParams = serverProperties.getAssessmentLevelParams(); - Map assessmentFilteredDetail = new HashMap<>(); - for (String assessmentParam : assessmentParams) { - if ((assessmentAllDetail.containsKey(assessmentParam))) { - assessmentFilteredDetail.put(assessmentParam, assessmentAllDetail.get(assessmentParam)); - } - } - readSectionLevelParams(assessmentAllDetail, assessmentFilteredDetail); - return assessmentFilteredDetail; - } - - private void readSectionLevelParams(Map assessmentAllDetail, - Map assessmentFilteredDetail) { - List> sectionResponse = new ArrayList<>(); - List sectionIdList = new ArrayList<>(); - List sectionParams = serverProperties.getAssessmentSectionParams(); - List> sections = (List>) assessmentAllDetail.get(Constants.CHILDREN); - for (Map section : sections) { - sectionIdList.add((String) section.get(Constants.IDENTIFIER)); - Map newSection = new HashMap<>(); - for (String sectionParam : sectionParams) { - if (section.containsKey(sectionParam)) { - newSection.put(sectionParam, section.get(sectionParam)); - } - } - List allQuestionIdList = new ArrayList<>(); - List> questions = (List>) section.get(Constants.CHILDREN); - for (Map question : questions) { - allQuestionIdList.add((String) question.get(Constants.IDENTIFIER)); - } - Collections.shuffle(allQuestionIdList); - List childNodeList = new ArrayList<>(); - if (!ObjectUtils.isEmpty(section.get(Constants.MAX_QUESTIONS))) { - int maxQuestions = (int) section.get(Constants.MAX_QUESTIONS); - childNodeList = allQuestionIdList.stream().limit(maxQuestions).collect(toList()); - } - newSection.put(Constants.CHILD_NODES, childNodeList); - sectionResponse.add(newSection); - } - assessmentFilteredDetail.put(Constants.CHILDREN, sectionResponse); - assessmentFilteredDetail.put(Constants.CHILD_NODES, sectionIdList); - } - - private List getQuestionIdList(Map questionListRequest) { - try { - if (questionListRequest.containsKey(Constants.REQUEST)) { - Map request = (Map) questionListRequest.get(Constants.REQUEST); - if ((!ObjectUtils.isEmpty(request)) && request.containsKey(Constants.SEARCH)) { - Map searchObj = (Map) request.get(Constants.SEARCH); - if (!ObjectUtils.isEmpty(searchObj) && searchObj.containsKey(Constants.IDENTIFIER) - && !CollectionUtils.isEmpty((List) searchObj.get(Constants.IDENTIFIER))) { - return (List) searchObj.get(Constants.IDENTIFIER); - } - } - } - } catch (Exception e) { - logger.error(String.format("Failed to process the questionList request body. %s", e.getMessage())); - } - return Collections.emptyList(); - } - - public Map createResponseMapWithProperStructure(Map hierarchySection, - Map resultMap) { - Map sectionLevelResult = new HashMap<>(); - sectionLevelResult.put(Constants.IDENTIFIER, hierarchySection.get(Constants.IDENTIFIER)); - sectionLevelResult.put(Constants.OBJECT_TYPE, hierarchySection.get(Constants.OBJECT_TYPE)); - sectionLevelResult.put(Constants.PRIMARY_CATEGORY, hierarchySection.get(Constants.PRIMARY_CATEGORY)); - sectionLevelResult.put(Constants.PASS_PERCENTAGE, hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE)); - Double result; - if (!ObjectUtils.isEmpty(resultMap)) { - result = (Double) resultMap.get(Constants.RESULT); - sectionLevelResult.put(Constants.RESULT, result); - sectionLevelResult.put(Constants.TOTAL, resultMap.get(Constants.TOTAL)); - sectionLevelResult.put(Constants.BLANK, resultMap.get(Constants.BLANK)); - sectionLevelResult.put(Constants.CORRECT, resultMap.get(Constants.CORRECT)); - sectionLevelResult.put(Constants.INCORRECT, resultMap.get(Constants.INCORRECT)); - } else { - result = 0.0; - sectionLevelResult.put(Constants.RESULT, result); - List childNodes = (List) hierarchySection.get(Constants.CHILDREN); - sectionLevelResult.put(Constants.TOTAL, childNodes.size()); - sectionLevelResult.put(Constants.BLANK, childNodes.size()); - sectionLevelResult.put(Constants.CORRECT, 0); - sectionLevelResult.put(Constants.INCORRECT, 0); - } - sectionLevelResult.put(Constants.PASS, - result >= ((Integer) hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE))); - sectionLevelResult.put(Constants.OVERALL_RESULT, result); - return sectionLevelResult; - } - - private SBApiResponse createDefaultResponse(String api) { - SBApiResponse response = new SBApiResponse(); - response.setId(api); - response.setVer(Constants.VER); - response.getParams().setResmsgid(UUID.randomUUID().toString()); - response.getParams().setStatus(Constants.SUCCESS); - response.setResponseCode(HttpStatus.OK); - response.setTs(DateTime.now().toString()); - return response; - } - - private Boolean validateQuestionListRequest(List identifierList, List questionsFromAssessment) { - return (new HashSet<>(questionsFromAssessment).containsAll(identifierList)) ? Boolean.TRUE : Boolean.FALSE; - } + private final Logger logger = LoggerFactory.getLogger(AssessmentServiceV2Impl.class); + + @Autowired + AssessmentUtilServiceV2 assessUtilServ; + + @Autowired + CbExtServerProperties serverProperties; + + @Autowired + Producer kafkaProducer; + + @Autowired + AssessmentRepository assessmentRepository; + + @Autowired + RequestInterceptor requestInterceptor; + + public SBApiResponse readAssessment(String assessmentIdentifier, String token) { + logger.info("AssessmentServiceV2Impl::readAssessment... Started"); + SBApiResponse response = createDefaultResponse(Constants.API_QUESTIONSET_HIERARCHY_GET); + String errMsg; + try { + String userId = validateAuthTokenAndFetchUserId(token); + if (userId != null) { + logger.info("readAssessment.. userId :" + userId); + Map assessmentAllDetail = new HashMap<>(); + errMsg = fetchReadHierarchyDetails(assessmentAllDetail, token, assessmentIdentifier); + if (errMsg.isEmpty() && !((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + logger.info("Fetched assessment Details... for : " + assessmentIdentifier); + List> existingDataList = assessmentRepository + .fetchUserAssessmentDataFromDB(userId, assessmentIdentifier); + Timestamp assessmentStartTime = new Timestamp(new Date().getTime()); + if (existingDataList.isEmpty()) { + logger.info("Assessment read first time for user."); + response.getResult().put(Constants.QUESTION_SET, readAssessmentLevelData(assessmentAllDetail)); + int expectedDuration = (Integer) assessmentAllDetail.get(Constants.EXPECTED_DURATION); + Boolean isAssessmentUpdatedToDB = assessmentRepository.addUserAssesmentDataToDB(userId, + assessmentIdentifier, assessmentStartTime, + calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime), + (Map) (response.getResult().get(Constants.QUESTION_SET)), + Constants.NOT_SUBMITTED); + if (Boolean.FALSE.equals(isAssessmentUpdatedToDB)) { + errMsg = Constants.ASSESSMENT_DATA_START_TIME_NOT_UPDATED; + } + } else { + logger.info("Assessment read... user has details... "); + Date existingAssessmentEndTime = (Date) (existingDataList.get(0).get(Constants.END_TIME)); + int time = assessmentStartTime.compareTo(existingAssessmentEndTime); + if (time < 0 && ((String) existingDataList.get(0).get(Constants.STATUS)) + .equalsIgnoreCase(Constants.NOT_SUBMITTED)) { + String questionSetFromAssessmentString = (String) existingDataList.get(0) + .get(Constants.ASSESSMENT_READ_RESPONSE); + Map questionSetFromAssessment = new Gson().fromJson( + questionSetFromAssessmentString, new TypeToken>() { + }.getType()); + response.getResult().put(Constants.QUESTION_SET, questionSetFromAssessment); + } else { + logger.info("Assessment read... adding user data to db..."); + response.getResult().put(Constants.QUESTION_SET, + readAssessmentLevelData(assessmentAllDetail)); + int expectedDuration = (Integer) assessmentAllDetail.get(Constants.EXPECTED_DURATION); + Boolean isAssessmentUpdatedToDB = assessmentRepository.addUserAssesmentDataToDB(userId, + assessmentIdentifier, assessmentStartTime, + calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime), + (Map) (response.getResult().get(Constants.QUESTION_SET)), + Constants.NOT_SUBMITTED); + if (Boolean.FALSE.equals(isAssessmentUpdatedToDB)) { + errMsg = Constants.ASSESSMENT_DATA_START_TIME_NOT_UPDATED; + } + } + } + } else if (errMsg.isEmpty() && ((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + response.getResult().put(Constants.QUESTION_SET, readAssessmentLevelData(assessmentAllDetail)); + } + } else { + errMsg = Constants.USER_ID_DOESNT_EXIST; + } + } catch (Exception e) { + logger.error(String.format("Exception in %s : %s", "read Assessment", e.getMessage()), e); + errMsg = "Failed to read Assessment. Exception: " + e.getMessage(); + } + if (StringUtils.isNotBlank(errMsg)) { + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(errMsg); + response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); + } + return response; + } + + public SBApiResponse readQuestionList(Map requestBody, String authUserToken) { + SBApiResponse response = createDefaultResponse(Constants.API_SUBMIT_ASSESSMENT); + String errMsg; + String primaryCategory = ""; + Map result = new HashMap<>(); + try { + List identifierList = new ArrayList<>(); + List questionList = new ArrayList<>(); + result = validateQuestionListAPI(requestBody, authUserToken, identifierList); + errMsg = result.get(Constants.ERROR_MESSAGE); + if (errMsg.isEmpty()) { + if (result.containsKey(Constants.PRIMARY_CATEGORY) && result.get(Constants.PRIMARY_CATEGORY).equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) + primaryCategory = result.get(Constants.PRIMARY_CATEGORY); + errMsg = assessUtilServ.fetchQuestionIdentifierValue(identifierList, questionList, primaryCategory); + if (errMsg.isEmpty() && identifierList.size() == questionList.size()) { + response.getResult().put(Constants.QUESTIONS, questionList); + } + } + } catch (Exception e) { + logger.error(String.format("Exception in %s : %s", "get Question List", e.getMessage()), e); + errMsg = "Failed to fetch the question list. Exception: " + e.getMessage(); + } + if (StringUtils.isNotBlank(errMsg)) { + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(errMsg); + response.setResponseCode(HttpStatus.BAD_REQUEST); + } + return response; + + } + + private String validateAuthTokenAndFetchUserId(String authUserToken) { + return requestInterceptor.fetchUserIdFromAccessToken(authUserToken); + } + + private String fetchReadHierarchyDetails(Map assessmentAllDetail, String token, + String assessmentIdentifier) { + Map readHierarchyApiResponse = assessUtilServ.getReadHierarchyApiResponse(assessmentIdentifier, + token); + if (readHierarchyApiResponse.isEmpty() + || !Constants.OK.equalsIgnoreCase((String) readHierarchyApiResponse.get(Constants.RESPONSE_CODE))) { + return Constants.ASSESSMENT_HIERARCHY_READ_FAILED; + } + assessmentAllDetail + .putAll((Map) ((Map) readHierarchyApiResponse.get(Constants.RESULT)) + .get(Constants.QUESTION_SET)); + + return StringUtils.EMPTY; + } + + private Map validateQuestionListAPI(Map requestBody, String authUserToken, + List identifierList) { + Map result = new HashMap<>(); + String userId = validateAuthTokenAndFetchUserId(authUserToken); + if (StringUtils.isBlank(userId)) { + result.put(Constants.ERROR_MESSAGE, Constants.USER_ID_DOESNT_EXIST); + return result; + } + + if (StringUtils.isBlank((String) requestBody.get(Constants.ASSESSMENT_ID_KEY))) { + result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_ID_KEY_IS_NOT_PRESENT_IS_EMPTY); + return result; + } + + identifierList.addAll(getQuestionIdList(requestBody)); + if (identifierList.isEmpty()) { + result.put(Constants.ERROR_MESSAGE, Constants.IDENTIFIER_LIST_IS_EMPTY); + return result; + } + + Map assessmentDetail = new HashMap<>(); + fetchReadHierarchyDetails(assessmentDetail, authUserToken, + (String) requestBody.get(Constants.ASSESSMENT_ID_KEY)); + + if (ObjectUtils.isEmpty(assessmentDetail)) { + result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_HIERARCHY_READ_FAILED); + return result; + } + + if (!((String) assessmentDetail.get(Constants.PRIMARY_CATEGORY)) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + (String) requestBody.get(Constants.ASSESSMENT_ID_KEY)); + String questionSetFromAssessmentString = (!existingDataList.isEmpty()) + ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) + : ""; + if (!questionSetFromAssessmentString.isEmpty()) { + Map questionSetFromAssessment = new Gson().fromJson(questionSetFromAssessmentString, + new TypeToken>() { + }.getType()); + List questionsFromAssessment = new ArrayList<>(); + List> sections = (List>) questionSetFromAssessment + .get(Constants.CHILDREN); + for (Map section : sections) { + questionsFromAssessment.addAll((List) section.get(Constants.CHILD_NODES)); + } + // Out of the list of questions received in the payload, checking if the request + // has only those ids which are a part of the user's latest assessment + // Fetching all the remaining questions details from the Redis + if (Boolean.FALSE.equals(validateQuestionListRequest(identifierList, questionsFromAssessment))) { + result.put(Constants.ERROR_MESSAGE, Constants.THE_QUESTIONS_IDS_PROVIDED_DONT_MATCH); + return result; + } + } else { + result.put(Constants.ERROR_MESSAGE, Constants.ASSESSMENT_ID_INVALID_SESSION_EXPIRED); + return result; + } + } else { + result.put(Constants.PRIMARY_CATEGORY, Constants.PRACTICE_QUESTION_SET); + } + result.put(Constants.ERROR_MESSAGE, ""); + return result; + } + + @Override + public SBApiResponse submitAssessment(Map submitRequest, String authUserToken) { + SBApiResponse outgoingResponse = createDefaultResponse(Constants.API_SUBMIT_ASSESSMENT); + String errMsg; + List> sectionListFromSubmitRequest = new ArrayList<>(); + List> hierarchySectionList = new ArrayList<>(); + Map allHierarchy = new HashMap<>(); + List questionsListFromAssessmentHierarchy = new ArrayList<>(); + errMsg = validateSubmitAssessmentRequest(submitRequest, authUserToken, hierarchySectionList, + sectionListFromSubmitRequest, allHierarchy); + if (errMsg.isEmpty()) { + String userId = validateAuthTokenAndFetchUserId(authUserToken); + String scoreCutOffType = ((String) allHierarchy.get(Constants.SCORE_CUTOFF_TYPE)).toLowerCase(); + List> existingDataList = new ArrayList<>(); + List> sectionLevelsResults = new ArrayList<>(); + for (Map hierarchySection : hierarchySectionList) { + String hierarchySectionId = (String) hierarchySection.get(Constants.IDENTIFIER); + String userSectionId = ""; + Map userSectionData = new HashMap<>(); + for (Map sectionFromSubmitRequest : sectionListFromSubmitRequest) { + userSectionId = (String) sectionFromSubmitRequest.get(Constants.IDENTIFIER); + if (userSectionId.equalsIgnoreCase(hierarchySectionId)) { + userSectionData = sectionFromSubmitRequest; + break; + } + } + if (!((String) (allHierarchy.get(Constants.PRIMARY_CATEGORY))) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { + existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + (String) submitRequest.get(Constants.IDENTIFIER)); + String questionSetFromAssessmentString = (!existingDataList.isEmpty()) + ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) + : ""; + if (!questionSetFromAssessmentString.isEmpty()) { + Map questionSetFromAssessment = new Gson() + .fromJson(questionSetFromAssessmentString, new TypeToken>() { + }.getType()); + if (questionSetFromAssessment != null + && questionSetFromAssessment.get(Constants.CHILDREN) != null) { + List> sections = (List>) questionSetFromAssessment + .get(Constants.CHILDREN); + for (Map section : sections) { + String sectionId = (String) section.get(Constants.IDENTIFIER); + if (userSectionId.equalsIgnoreCase(sectionId)) { + questionsListFromAssessmentHierarchy = (List) section + .get(Constants.CHILD_NODES); + break; + } + } + } else { + errMsg = "Question Set From The Database returns Null"; + outgoingResponse.getResult().clear(); + break; + } + + hierarchySection.put(Constants.SCORE_CUTOFF_TYPE, scoreCutOffType); + List> questionsListFromSubmitRequest = new ArrayList<>(); + if (userSectionData.containsKey(Constants.CHILDREN) + && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { + questionsListFromSubmitRequest = (List>) userSectionData + .get(Constants.CHILDREN); + } + Map result = new HashMap<>(); + switch (scoreCutOffType) { + case Constants.ASSESSMENT_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + outgoingResponse.getResult().putAll(calculateAssessmentFinalResults(result)); + writeDataToDatabaseAndTriggerKafkaEvent(submitRequest, userId, existingDataList, result, + (String) allHierarchy.get(Constants.PRIMARY_CATEGORY)); + return outgoingResponse; + } + case Constants.SECTION_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + sectionLevelsResults.add(result); + } + break; + default: + break; + } + } + } else { + hierarchySection.put(Constants.SCORE_CUTOFF_TYPE, scoreCutOffType); + List> questionsListFromSubmitRequest = new ArrayList<>(); + if (userSectionData.containsKey(Constants.CHILDREN) + && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { + questionsListFromSubmitRequest = (List>) userSectionData + .get(Constants.CHILDREN); + } + List desiredKeys = Lists.newArrayList(Constants.IDENTIFIER); + List questionsList = questionsListFromSubmitRequest.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); + questionsListFromAssessmentHierarchy = questionsList.stream() + .map(object -> Objects.toString(object, null)).collect(Collectors.toList()); + Map result = new HashMap<>(); + switch (scoreCutOffType) { + case Constants.ASSESSMENT_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + outgoingResponse.getResult().putAll(calculateAssessmentFinalResults(result)); + return outgoingResponse; + } + case Constants.SECTION_LEVEL_SCORE_CUTOFF: { + result.putAll(createResponseMapWithProperStructure(hierarchySection, + assessUtilServ.validateQumlAssessment(questionsListFromAssessmentHierarchy, + questionsListFromSubmitRequest))); + sectionLevelsResults.add(result); + } + break; + default: + break; + } + } + } + if (errMsg.isEmpty() && !ObjectUtils.isEmpty(scoreCutOffType) + && scoreCutOffType.equalsIgnoreCase(Constants.SECTION_LEVEL_SCORE_CUTOFF)) { + Map result = calculateSectionFinalResults(sectionLevelsResults); + outgoingResponse.getResult().putAll(result); + writeDataToDatabaseAndTriggerKafkaEvent(submitRequest, userId, existingDataList, result, + (String) allHierarchy.get(Constants.PRIMARY_CATEGORY)); + return outgoingResponse; + } + } + if (StringUtils.isNotBlank(errMsg)) { + outgoingResponse.getParams().setStatus(Constants.FAILED); + outgoingResponse.getParams().setErrmsg(errMsg); + outgoingResponse.setResponseCode(HttpStatus.BAD_REQUEST); + } + return outgoingResponse; + } + + private void writeDataToDatabaseAndTriggerKafkaEvent(Map submitRequest, String userId, + List> existingDataList, Map result, String primaryCategory) { + Date startTime = (!existingDataList.isEmpty()) ? (Date) existingDataList.get(0).get(Constants.START_TIME) + : null; + Boolean isAssessmentUpdatedToDB = assessmentRepository.updateUserAssesmentDataToDB(userId, + (String) submitRequest.get(Constants.IDENTIFIER), submitRequest, result, Constants.SUBMITTED, + startTime); + if (Boolean.TRUE.equals(isAssessmentUpdatedToDB)) { + Map kafkaResult = new HashMap<>(); + kafkaResult.put(Constants.CONTENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); + kafkaResult.put(Constants.COURSE_ID, submitRequest.get(Constants.COURSE_ID) != null ? submitRequest.get(Constants.COURSE_ID) : ""); + kafkaResult.put(Constants.BATCH_ID, submitRequest.get(Constants.BATCH_ID) != null ? submitRequest.get(Constants.BATCH_ID) : ""); + kafkaResult.put(Constants.USER_ID, submitRequest.get(Constants.USER_ID)); + kafkaResult.put(Constants.ASSESSMENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); + kafkaResult.put(Constants.PRIMARY_CATEGORY, primaryCategory); + kafkaProducer.push(serverProperties.getAssessmentSubmitTopic(), kafkaResult); + } + } + + private String validateSubmitAssessmentRequest(Map submitRequest, String authUserToken, + List> hierarchySectionList, List> sectionListFromSubmitRequest, + Map assessmentHierarchy) { + String userId = validateAuthTokenAndFetchUserId(authUserToken); + if (ObjectUtils.isEmpty(userId)) { + return Constants.USER_ID_DOESNT_EXIST; + } + submitRequest.put(Constants.USER_ID, userId); + if (StringUtils.isEmpty((String) submitRequest.get(Constants.IDENTIFIER))) { + return Constants.INVALID_ASSESSMENT_ID; + } + String assessmentIdFromRequest = (String) submitRequest.get(Constants.IDENTIFIER); + String errMsg = fetchReadHierarchyDetails(assessmentHierarchy, authUserToken, assessmentIdFromRequest); + if (!errMsg.isEmpty()) { + return errMsg; + } + if (ObjectUtils.isEmpty(assessmentHierarchy)) { + return Constants.READ_ASSESSMENT_FAILED; + } + hierarchySectionList.addAll((List>) assessmentHierarchy.get(Constants.CHILDREN)); + sectionListFromSubmitRequest.addAll((List>) submitRequest.get(Constants.CHILDREN)); + if (((String) (assessmentHierarchy.get(Constants.PRIMARY_CATEGORY))) + .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) + return ""; + List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + assessmentIdFromRequest); + Date assessmentStartTime = (!existingDataList.isEmpty()) + ? (Date) existingDataList.get(0).get(Constants.START_TIME) + : null; + if (assessmentStartTime == null) { + return Constants.READ_ASSESSMENT_START_TIME_FAILED; + } + int expectedDuration = (Integer) assessmentHierarchy.get(Constants.EXPECTED_DURATION); + Timestamp later = calculateAssessmentSubmitTime(expectedDuration, assessmentStartTime); + Timestamp submissionTime = new Timestamp(new Date().getTime()); + int time = submissionTime.compareTo(later); + if (time <= 0) { + List desiredKeys = Lists.newArrayList(Constants.IDENTIFIER); + List hierarchySectionIds = hierarchySectionList.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); + List submitSectionIds = sectionListFromSubmitRequest.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)).collect(toList()); + if (!new HashSet<>(hierarchySectionIds).containsAll(submitSectionIds)) { + return Constants.WRONG_SECTION_DETAILS; + } else { + String areQuestionIdsSame = validateIfQuestionIdsAreSame(submitRequest, sectionListFromSubmitRequest, + desiredKeys, userId); + if (!areQuestionIdsSame.isEmpty()) + return areQuestionIdsSame; + } + } else { + return Constants.ASSESSMENT_SUBMIT_EXPIRED; + } + return ""; + } + + private String validateIfQuestionIdsAreSame(Map submitRequest, + List> sectionListFromSubmitRequest, List desiredKeys, String userId) { + List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + (String) submitRequest.get(Constants.IDENTIFIER)); + String questionSetFromAssessmentString = (!existingDataList.isEmpty()) + ? (String) existingDataList.get(0).get(Constants.ASSESSMENT_READ_RESPONSE) + : ""; + if (!questionSetFromAssessmentString.isEmpty()) { + Map questionSetFromAssessment = new Gson().fromJson(questionSetFromAssessmentString, + new TypeToken>() { + }.getType()); + if (questionSetFromAssessment != null && questionSetFromAssessment.get(Constants.CHILDREN) != null) { + List> sections = (List>) questionSetFromAssessment + .get(Constants.CHILDREN); + List desiredKey = Lists.newArrayList(Constants.CHILD_NODES); + List questionList = sections.stream() + .flatMap(x -> desiredKey.stream().filter(x::containsKey).map(x::get)).collect(toList()); + List questionIdsFromAssessmentHierarchy = new ArrayList<>(); + List> questionsListFromSubmitRequest = new ArrayList<>(); + for (Object question : questionList) { + questionIdsFromAssessmentHierarchy.addAll((List) question); + } + for (Map userSectionData : sectionListFromSubmitRequest) { + if (userSectionData.containsKey(Constants.CHILDREN) + && !ObjectUtils.isEmpty(userSectionData.get(Constants.CHILDREN))) { + questionsListFromSubmitRequest + .addAll((List>) userSectionData.get(Constants.CHILDREN)); + } + } + List userQuestionIdsFromSubmitRequest = questionsListFromSubmitRequest.stream() + .flatMap(x -> desiredKeys.stream().filter(x::containsKey).map(x::get)) + .collect(Collectors.toList()); + if (!new HashSet<>(questionIdsFromAssessmentHierarchy).containsAll(userQuestionIdsFromSubmitRequest)) { + return Constants.ASSESSMENT_SUBMIT_INVALID_QUESTION; + } + } + } else { + return Constants.ASSESSMENT_SUBMIT_QUESTION_READ_FAILED; + } + return ""; + } + + private Timestamp calculateAssessmentSubmitTime(int expectedDuration, Date assessmentStartTime) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(new Timestamp(assessmentStartTime.getTime()).getTime()); + if (serverProperties.getUserAssessmentSubmissionDuration().isEmpty()) { + serverProperties.setUserAssessmentSubmissionDuration("120"); + } + cal.add(Calendar.SECOND, + expectedDuration + Integer.parseInt(serverProperties.getUserAssessmentSubmissionDuration())); + return new Timestamp(cal.getTime().getTime()); + } + + private Map calculateAssessmentFinalResults(Map assessmentLevelResult) { + Map res = new HashMap<>(); + try { + res.put(Constants.CHILDREN, Collections.singletonList(assessmentLevelResult)); + Double result = (Double) assessmentLevelResult.get(Constants.RESULT); + res.put(Constants.OVERALL_RESULT, result); + res.put(Constants.TOTAL, assessmentLevelResult.get(Constants.TOTAL)); + res.put(Constants.BLANK, assessmentLevelResult.get(Constants.BLANK)); + res.put(Constants.CORRECT, assessmentLevelResult.get(Constants.CORRECT)); + res.put(Constants.PASS_PERCENTAGE, assessmentLevelResult.get(Constants.PASS_PERCENTAGE)); + res.put(Constants.INCORRECT, assessmentLevelResult.get(Constants.INCORRECT)); + Integer minimumPassPercentage = (Integer) assessmentLevelResult.get(Constants.PASS_PERCENTAGE); + res.put(Constants.PASS, result >= minimumPassPercentage); + } catch (Exception e) { + logger.info(e.getMessage()); + } + return res; + } + + private Map calculateSectionFinalResults(List> sectionLevelResults) { + Map res = new HashMap<>(); + Double result; + Integer correct = 0; + Integer blank = 0; + Integer inCorrect = 0; + Integer total = 0; + int pass = 0; + Double totalResult = 0.0; + try { + for (Map sectionChildren : sectionLevelResults) { + res.put(Constants.CHILDREN, sectionLevelResults); + result = (Double) sectionChildren.get(Constants.RESULT); + totalResult += result; + total += (Integer) sectionChildren.get(Constants.TOTAL); + blank += (Integer) sectionChildren.get(Constants.BLANK); + correct += (Integer) sectionChildren.get(Constants.CORRECT); + inCorrect += (Integer) sectionChildren.get(Constants.INCORRECT); + Integer minimumPassPercentage = (Integer) sectionChildren.get(Constants.PASS_PERCENTAGE); + if (result >= minimumPassPercentage) { + pass++; + } + } + res.put(Constants.OVERALL_RESULT, totalResult / sectionLevelResults.size()); + res.put(Constants.TOTAL, total); + res.put(Constants.BLANK, blank); + res.put(Constants.CORRECT, correct); + res.put(Constants.INCORRECT, inCorrect); + res.put(Constants.PASS, (pass == sectionLevelResults.size())); + } catch (Exception e) { + logger.info(e.getMessage()); + } + return res; + } + + private Map readAssessmentLevelData(Map assessmentAllDetail) { + List assessmentParams = serverProperties.getAssessmentLevelParams(); + Map assessmentFilteredDetail = new HashMap<>(); + for (String assessmentParam : assessmentParams) { + if ((assessmentAllDetail.containsKey(assessmentParam))) { + assessmentFilteredDetail.put(assessmentParam, assessmentAllDetail.get(assessmentParam)); + } + } + readSectionLevelParams(assessmentAllDetail, assessmentFilteredDetail); + return assessmentFilteredDetail; + } + + private void readSectionLevelParams(Map assessmentAllDetail, + Map assessmentFilteredDetail) { + List> sectionResponse = new ArrayList<>(); + List sectionIdList = new ArrayList<>(); + List sectionParams = serverProperties.getAssessmentSectionParams(); + List> sections = (List>) assessmentAllDetail.get(Constants.CHILDREN); + for (Map section : sections) { + sectionIdList.add((String) section.get(Constants.IDENTIFIER)); + Map newSection = new HashMap<>(); + for (String sectionParam : sectionParams) { + if (section.containsKey(sectionParam)) { + newSection.put(sectionParam, section.get(sectionParam)); + } + } + List allQuestionIdList = new ArrayList<>(); + List> questions = (List>) section.get(Constants.CHILDREN); + for (Map question : questions) { + allQuestionIdList.add((String) question.get(Constants.IDENTIFIER)); + } + Collections.shuffle(allQuestionIdList); + List childNodeList = new ArrayList<>(); + if (!ObjectUtils.isEmpty(section.get(Constants.MAX_QUESTIONS))) { + int maxQuestions = (int) section.get(Constants.MAX_QUESTIONS); + childNodeList = allQuestionIdList.stream().limit(maxQuestions).collect(toList()); + } + newSection.put(Constants.CHILD_NODES, childNodeList); + sectionResponse.add(newSection); + } + assessmentFilteredDetail.put(Constants.CHILDREN, sectionResponse); + assessmentFilteredDetail.put(Constants.CHILD_NODES, sectionIdList); + } + + private List getQuestionIdList(Map questionListRequest) { + try { + if (questionListRequest.containsKey(Constants.REQUEST)) { + Map request = (Map) questionListRequest.get(Constants.REQUEST); + if ((!ObjectUtils.isEmpty(request)) && request.containsKey(Constants.SEARCH)) { + Map searchObj = (Map) request.get(Constants.SEARCH); + if (!ObjectUtils.isEmpty(searchObj) && searchObj.containsKey(Constants.IDENTIFIER) + && !CollectionUtils.isEmpty((List) searchObj.get(Constants.IDENTIFIER))) { + return (List) searchObj.get(Constants.IDENTIFIER); + } + } + } + } catch (Exception e) { + logger.error(String.format("Failed to process the questionList request body. %s", e.getMessage())); + } + return Collections.emptyList(); + } + + public Map createResponseMapWithProperStructure(Map hierarchySection, + Map resultMap) { + Map sectionLevelResult = new HashMap<>(); + sectionLevelResult.put(Constants.IDENTIFIER, hierarchySection.get(Constants.IDENTIFIER)); + sectionLevelResult.put(Constants.OBJECT_TYPE, hierarchySection.get(Constants.OBJECT_TYPE)); + sectionLevelResult.put(Constants.PRIMARY_CATEGORY, hierarchySection.get(Constants.PRIMARY_CATEGORY)); + sectionLevelResult.put(Constants.PASS_PERCENTAGE, hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE)); + Double result; + if (!ObjectUtils.isEmpty(resultMap)) { + result = (Double) resultMap.get(Constants.RESULT); + sectionLevelResult.put(Constants.RESULT, result); + sectionLevelResult.put(Constants.TOTAL, resultMap.get(Constants.TOTAL)); + sectionLevelResult.put(Constants.BLANK, resultMap.get(Constants.BLANK)); + sectionLevelResult.put(Constants.CORRECT, resultMap.get(Constants.CORRECT)); + sectionLevelResult.put(Constants.INCORRECT, resultMap.get(Constants.INCORRECT)); + } else { + result = 0.0; + sectionLevelResult.put(Constants.RESULT, result); + List childNodes = (List) hierarchySection.get(Constants.CHILDREN); + sectionLevelResult.put(Constants.TOTAL, childNodes.size()); + sectionLevelResult.put(Constants.BLANK, childNodes.size()); + sectionLevelResult.put(Constants.CORRECT, 0); + sectionLevelResult.put(Constants.INCORRECT, 0); + } + sectionLevelResult.put(Constants.PASS, + result >= ((Integer) hierarchySection.get(Constants.MINIMUM_PASS_PERCENTAGE))); + sectionLevelResult.put(Constants.OVERALL_RESULT, result); + return sectionLevelResult; + } + + private SBApiResponse createDefaultResponse(String api) { + SBApiResponse response = new SBApiResponse(); + response.setId(api); + response.setVer(Constants.VER); + response.getParams().setResmsgid(UUID.randomUUID().toString()); + response.getParams().setStatus(Constants.SUCCESS); + response.setResponseCode(HttpStatus.OK); + response.setTs(DateTime.now().toString()); + return response; + } + + private Boolean validateQuestionListRequest(List identifierList, List questionsFromAssessment) { + return (new HashSet<>(questionsFromAssessment).containsAll(identifierList)) ? Boolean.TRUE : Boolean.FALSE; + } } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index aba381b73..1108d2f76 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -39,7 +39,7 @@ lms.user.read.path=/private/user/v1/read/ lms.user.update.path=/private/user/v1/update progress.api.endpoint=v1/content/state/read participants.api.endpoint=v1/batch/participants/list -sb.api.key=apiKey +sb.api.key= #Elastic search config es.auth.enabled=false @@ -154,16 +154,16 @@ redis.port=6379 #redis timeout value is in seconds redis.timeout=84600 -#Assessment Feature values +Assessment Feature values assessment.host=http://assessment-service:9000/ assessment.hierarchy.read.path=questionset/v4/hierarchy/{identifier}?mode=edit assessment.question.list.path=question/v4/list -#assessment.host=https://igot-dev.in/api/ +#assessment.host=https://portal.igot-dev.in/api/ #assessment.hierarchy.read.path=questionset/v1/hierarchy/{identifier}?hierarchy=detail -#assessment.question.list.path=question/v/list -assessment.read.assessmentLevel.params=name,identifier,primaryCategory,versionKey,mimeType,code,version,objectType,status,expectedDuration,totalQuestions,maxQuestions,description -assessment.read.sectionLevel.params=parent,name,identifier,description,trackable,primaryCategory,versionKey,mimeType,code,version,objectType,status,index,maxQuestions,scoreCutoffType,minimumPassPercentage,additionalInstructions +#assessment.question.list.path=question/v1/list +assessment.read.assessmentLevel.params=name,identifier,primaryCategory,versionKey,mimeType,code,version,objectType,status,expectedDuration,totalQuestions,maxQuestions,description,retakeAssessmentDuration +assessment.read.sectionLevel.params=parent,name,identifier,description,trackable,primaryCategory,versionKey,mimeType,code,version,objectType,status,index,maxQuestions,scoreCutoffType,minimumPassPercentage,additionalInstructions,retakeAssessmentDuration assessment.read.questionLevel.params=parent,name,identifier,primaryCategory,body,versionKey,mimeType,code,objectType,status,qType,index,showSolutions,allowAnonymousAccess,visibility,version,showFeedback,license assessment.read.min.question.params=parent,name,identifier,primaryCategory,versionKey,mimeType,objectType,qType @@ -195,7 +195,7 @@ sso.password=admin sunbird_sso_publickey=publicKey sso.username=admin sunbird_sso_client_secret=clientSecretValue -accesstoken.publickey.basepath=publicKeyPath +accesstoken.publickey.basepath=/home/juhi/U2JTvZDDV8xo7fk_4wuc-d5Rf64OLmhziQEHcGnUshM user.assessment.submission.duration=120 #User Registration Feature From 10dcdd07285d3c749d4affb5582d7467a7172d1f Mon Sep 17 00:00:00 2001 From: Juhi Date: Tue, 6 Dec 2022 13:01:24 +0530 Subject: [PATCH 10/13] final changes retake assessment --- .../controller/AssessmentController.java | 8 + .../sunbird/assessment/model/Competency.java | 196 ++++++++++++++++++ .../assessment/repo/AssessmentRepository.java | 2 +- .../repo/AssessmentRepositoryImpl.java | 6 +- .../service/AssessmentServiceV2.java | 2 + .../service/AssessmentServiceV2Impl.java | 63 +++++- .../org/sunbird/common/util/Constants.java | 4 + 7 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/sunbird/assessment/model/Competency.java diff --git a/src/main/java/org/sunbird/assessment/controller/AssessmentController.java b/src/main/java/org/sunbird/assessment/controller/AssessmentController.java index ec5b3bb08..ac0bc89e8 100644 --- a/src/main/java/org/sunbird/assessment/controller/AssessmentController.java +++ b/src/main/java/org/sunbird/assessment/controller/AssessmentController.java @@ -147,6 +147,14 @@ public ResponseEntity readQuestionList(@Valid @RequestBody Map(response, response.getResponseCode()); } + + @GetMapping("/v1/quml/assessment/retake/{assessmentIdentifier}") + public ResponseEntity retakeAssessment( + @PathVariable("assessmentIdentifier") String assessmentIdentifier, + @RequestHeader(Constants.X_AUTH_TOKEN) String token) throws Exception { + SBApiResponse readResponse = assessmentServiceV2.retakeAssessment(assessmentIdentifier, token); + return new ResponseEntity<>(readResponse, readResponse.getResponseCode()); + } // QUML based Assessment APIs // ======================= } diff --git a/src/main/java/org/sunbird/assessment/model/Competency.java b/src/main/java/org/sunbird/assessment/model/Competency.java new file mode 100644 index 000000000..ba505aff3 --- /dev/null +++ b/src/main/java/org/sunbird/assessment/model/Competency.java @@ -0,0 +1,196 @@ +package org.sunbird.assessment.model; + +import javax.annotation.Generated; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ +"id", +"name", +"description", +"competencyType", +"competencyArea", +"source", +"selectedLevelId", +"selectedLevelLevel", +"selectedLevelName", +"selectedLevelDescription" +}) +public class Competency { + +@JsonProperty("id") +private String id; +@JsonProperty("name") +private String name; +@JsonProperty("description") +private String description; +@JsonProperty("competencyType") +private String competencyType; +@JsonProperty("competencyArea") +private String competencyArea; +@JsonProperty("source") +private String source; +@JsonProperty("selectedLevelId") +private String selectedLevelId; +@JsonProperty("selectedLevelLevel") +private String selectedLevelLevel; +@JsonProperty("selectedLevelName") +private String selectedLevelName; +@JsonProperty("selectedLevelDescription") +private String selectedLevelDescription; + +@JsonProperty("id") +public String getId() { +return id; +} + +@JsonProperty("id") +public void setId(String id) { +this.id = id; +} + +@JsonProperty("name") +public String getName() { +return name; +} + +@JsonProperty("name") +public void setName(String name) { +this.name = name; +} + +@JsonProperty("description") +public String getDescription() { +return description; +} + +@JsonProperty("description") +public void setDescription(String description) { +this.description = description; +} + +@JsonProperty("competencyType") +public String getCompetencyType() { +return competencyType; +} + +@JsonProperty("competencyType") +public void setCompetencyType(String competencyType) { +this.competencyType = competencyType; +} + +@JsonProperty("competencyArea") +public String getCompetencyArea() { +return competencyArea; +} + +@JsonProperty("competencyArea") +public void setCompetencyArea(String competencyArea) { +this.competencyArea = competencyArea; +} + +@JsonProperty("source") +public String getSource() { +return source; +} + +@JsonProperty("source") +public void setSource(String source) { +this.source = source; +} + +@JsonProperty("selectedLevelId") +public String getSelectedLevelId() { +return selectedLevelId; +} + +@JsonProperty("selectedLevelId") +public void setSelectedLevelId(String selectedLevelId) { +this.selectedLevelId = selectedLevelId; +} + +@JsonProperty("selectedLevelLevel") +public String getSelectedLevelLevel() { +return selectedLevelLevel; +} + +@JsonProperty("selectedLevelLevel") +public void setSelectedLevelLevel(String selectedLevelLevel) { +this.selectedLevelLevel = selectedLevelLevel; +} + +@JsonProperty("selectedLevelName") +public String getSelectedLevelName() { +return selectedLevelName; +} + +@JsonProperty("selectedLevelName") +public void setSelectedLevelName(String selectedLevelName) { +this.selectedLevelName = selectedLevelName; +} + +@JsonProperty("selectedLevelDescription") +public String getSelectedLevelDescription() { +return selectedLevelDescription; +} + +@JsonProperty("selectedLevelDescription") +public void setSelectedLevelDescription(String selectedLevelDescription) { +this.selectedLevelDescription = selectedLevelDescription; +} + +@Override +public String toString() { +StringBuilder sb = new StringBuilder(); +sb.append(Competency.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); +sb.append("id"); +sb.append('='); +sb.append(((this.id == null)?"":this.id)); +sb.append(','); +sb.append("name"); +sb.append('='); +sb.append(((this.name == null)?"":this.name)); +sb.append(','); +sb.append("description"); +sb.append('='); +sb.append(((this.description == null)?"":this.description)); +sb.append(','); +sb.append("competencyType"); +sb.append('='); +sb.append(((this.competencyType == null)?"":this.competencyType)); +sb.append(','); +sb.append("competencyArea"); +sb.append('='); +sb.append(((this.competencyArea == null)?"":this.competencyArea)); +sb.append(','); +sb.append("source"); +sb.append('='); +sb.append(((this.source == null)?"":this.source)); +sb.append(','); +sb.append("selectedLevelId"); +sb.append('='); +sb.append(((this.selectedLevelId == null)?"":this.selectedLevelId)); +sb.append(','); +sb.append("selectedLevelLevel"); +sb.append('='); +sb.append(((this.selectedLevelLevel == null)?"":this.selectedLevelLevel)); +sb.append(','); +sb.append("selectedLevelName"); +sb.append('='); +sb.append(((this.selectedLevelName == null)?"":this.selectedLevelName)); +sb.append(','); +sb.append("selectedLevelDescription"); +sb.append('='); +sb.append(((this.selectedLevelDescription == null)?"":this.selectedLevelDescription)); +sb.append(','); +if (sb.charAt((sb.length()- 1)) == ',') { +sb.setCharAt((sb.length()- 1), ']'); +} else { +sb.append(']'); +} +return sb.toString(); +} + +} \ No newline at end of file diff --git a/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java b/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java index d89784878..73ed6d383 100644 --- a/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java +++ b/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java @@ -56,5 +56,5 @@ boolean addUserAssesmentDataToDB(String userId, String assessmentId, Timestamp s Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdentifier, Map submitAssessmentRequest, Map submitAssessmentResponse, String status, - Date startTime); + Date startTime, Date submitTime); } diff --git a/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java b/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java index 66f75fc49..fb14c7905 100644 --- a/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java +++ b/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java @@ -161,7 +161,7 @@ public List> fetchUserAssessmentDataFromDB(String userId, St @Override public Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdentifier, Map submitAssessmentRequest, Map submitAssessmentResponse, String status, - Date startTime) { + Date startTime, Date submitTime) { Map compositeKeys = new HashMap<>(); compositeKeys.put(Constants.USER_ID, userId); compositeKeys.put(Constants.ASSESSMENT_ID_KEY, assessmentIdentifier); @@ -176,6 +176,10 @@ public Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdent if (!status.isEmpty()) { fieldsToBeUpdated.put(Constants.STATUS, status); } + if (submitTime!=null) + { + fieldsToBeUpdated.put(Constants.SUBMIT_TIME, submitTime); + } cassandraOperation.updateRecord(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_USER_ASSESSMENT_DATA, fieldsToBeUpdated, compositeKeys); return true; diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2.java index 84a9b3ec0..e6159dbfd 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2.java @@ -17,4 +17,6 @@ public interface AssessmentServiceV2 { public SBApiResponse readAssessment(String assessmentIdentifier, String token) throws Exception; public SBApiResponse readQuestionList(Map requestBody, String authUserToken) throws Exception; + + public SBApiResponse retakeAssessment(String assessmentIdentifier, String token) throws Exception; } diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java index 2bde14ab1..982f79eeb 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java @@ -2,6 +2,7 @@ import static java.util.stream.Collectors.toList; +import java.lang.reflect.Type; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; @@ -13,6 +14,7 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; @@ -24,6 +26,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; +import org.sunbird.assessment.model.Competency; import org.sunbird.assessment.repo.AssessmentRepository; import org.sunbird.common.model.SBApiResponse; import org.sunbird.common.util.CbExtServerProperties; @@ -385,7 +388,7 @@ private void writeDataToDatabaseAndTriggerKafkaEvent(Map submitR : null; Boolean isAssessmentUpdatedToDB = assessmentRepository.updateUserAssesmentDataToDB(userId, (String) submitRequest.get(Constants.IDENTIFIER), submitRequest, result, Constants.SUBMITTED, - startTime); + startTime, new Timestamp(new Date().getTime())); if (Boolean.TRUE.equals(isAssessmentUpdatedToDB)) { Map kafkaResult = new HashMap<>(); kafkaResult.put(Constants.CONTENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); @@ -394,6 +397,12 @@ private void writeDataToDatabaseAndTriggerKafkaEvent(Map submitR kafkaResult.put(Constants.USER_ID, submitRequest.get(Constants.USER_ID)); kafkaResult.put(Constants.ASSESSMENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); kafkaResult.put(Constants.PRIMARY_CATEGORY, primaryCategory); + List competencies = new ArrayList<>(); + if ((primaryCategory.equalsIgnoreCase("Competency Assessment") && submitRequest.containsKey("competencies_v3") && submitRequest.get("competencies_v3") != null)) { + competencies = new Gson().fromJson((String) submitRequest.get("competencies_v3"), (Type) Competency[].class); + } + kafkaResult.put(Constants.COMPETENCY, competencies.isEmpty() ? "" : competencies); + logger.info(kafkaResult.toString()); kafkaProducer.push(serverProperties.getAssessmentSubmitTopic(), kafkaResult); } } @@ -667,4 +676,56 @@ private SBApiResponse createDefaultResponse(String api) { private Boolean validateQuestionListRequest(List identifierList, List questionsFromAssessment) { return (new HashSet<>(questionsFromAssessment).containsAll(identifierList)) ? Boolean.TRUE : Boolean.FALSE; } + + public SBApiResponse retakeAssessment(String assessmentIdentifier, String token) throws Exception { + logger.info("AssessmentServiceV2Impl::retakeAssessment... Started"); + SBApiResponse response = createDefaultResponse(Constants.API_RETAKE_ASSESSMENT_GET); + String errMsg = ""; + try { + String userId = validateAuthTokenAndFetchUserId(token); + if (userId != null) { + List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, + assessmentIdentifier); + if (!existingDataList.isEmpty()) { + Date assessmentEndTime = (!existingDataList.isEmpty()) + ? (Date) existingDataList.get(0).get(Constants.END_TIME) + : null; + if (assessmentEndTime == null) { + errMsg = Constants.READ_ASSESSMENT_START_TIME_FAILED; + } else { + Map assessmentAllDetail = new HashMap<>(); + errMsg = fetchReadHierarchyDetails(assessmentAllDetail, token, assessmentIdentifier); + if (errMsg.isEmpty() && (assessmentAllDetail.get(Constants.RETAKE_ASSESSMENT_DURATION)) != null) { + long time = calculateAssessmentRetakeTime((int) assessmentAllDetail.get(Constants.RETAKE_ASSESSMENT_DURATION), assessmentEndTime); + if (time > 0) + errMsg = "You can retake this assessment after " + time + " seconds"; + } + } + } + } else { + errMsg = Constants.USER_ID_DOESNT_EXIST; + } + } catch (Exception e) { + logger.error(String.format("Exception in %s : %s", "read Assessment", e.getMessage()), e); + errMsg = "Failed to read Assessment. Exception: " + e.getMessage(); + } + if (StringUtils.isNotBlank(errMsg)) { + response.getParams().setStatus(Constants.FAILED); + response.getParams().setErrmsg(errMsg); + response.setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR); + } + return response; + } + + private long calculateAssessmentRetakeTime(int retakeAssessmentDuration, Date assessmentEndTime) { + Calendar retakeAssessmentTime = Calendar.getInstance(); + retakeAssessmentTime.setTimeInMillis(new Timestamp(assessmentEndTime.getTime()).getTime()); + retakeAssessmentTime.add(Calendar.MINUTE, + retakeAssessmentDuration); + Calendar now = Calendar.getInstance(); + if (now.getTime().compareTo(retakeAssessmentTime.getTime())<0) { + return TimeUnit.MILLISECONDS.toSeconds(Math.abs(retakeAssessmentTime.getTimeInMillis() - now.getTimeInMillis())); + } + return 0; + } } \ 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 2432da60b..8be920c4c 100644 --- a/src/main/java/org/sunbird/common/util/Constants.java +++ b/src/main/java/org/sunbird/common/util/Constants.java @@ -366,6 +366,8 @@ public class Constants { public static final String MTF_QUESTION = "MTF Question"; public static final String FTB_QUESTION = "FTB Question"; public static final String API_QUESTIONSET_HIERARCHY_GET = "api.questionset.hierarchy.get"; + + public static final String API_RETAKE_ASSESSMENT_GET = "api.retake.assessment.get"; public static final String VER = "3.0"; public static final String API_QUESTIONS_LIST = "api.questions.list"; public static final String MINIMUM_PASS_PERCENTAGE = "minimumPassPercentage"; @@ -577,6 +579,8 @@ public class Constants { public static final String ERROR_INVALID_USER_ID = "Invalid UserId"; public static final String ERROR_INVALID_REQUEST_BODY = "Invalid Request Body"; public static final String CLIENT_ERROR = "CLIENT_ERROR"; + public static final String RETAKE_ASSESSMENT_DURATION = "retakeAssessmentDuration"; + public static final String SUBMIT_TIME = "submittime"; private Constants() { throw new IllegalStateException("Utility class"); From 449942d11ba3e5df1c1ef0748bfb65d8adc1afae Mon Sep 17 00:00:00 2001 From: Juhi Date: Tue, 6 Dec 2022 15:55:51 +0530 Subject: [PATCH 11/13] final changes retake assessment --- src/main/java/org/sunbird/common/util/Constants.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/sunbird/common/util/Constants.java b/src/main/java/org/sunbird/common/util/Constants.java index 4aefc6ce1..587da9520 100644 --- a/src/main/java/org/sunbird/common/util/Constants.java +++ b/src/main/java/org/sunbird/common/util/Constants.java @@ -533,7 +533,6 @@ public class Constants { public static final String PARENT_CONTENT_TYPE = "parentContentType"; public static final String NEW_COURSES = "newcourses"; public static final String OVERVIEW_BATCH_KEY = "/overview?batchId="; -<<<<<<< HEAD public static final String PRACTICE_QUESTION_SET = "Practice Question Set"; public static final String EXPECTED_DURATION = "expectedDuration"; public static final String SUBMITTED = "SUBMITTED"; From e29e0d200f0d60f49e2f3752747e340e78c74dff Mon Sep 17 00:00:00 2001 From: Juhi Date: Wed, 7 Dec 2022 12:40:04 +0530 Subject: [PATCH 12/13] final changes retake assessment --- .../org/sunbird/assessment/RedisCacheMgr.java | 144 ++++++++++++++++++ .../assessment/repo/AssessmentRepository.java | 2 +- .../repo/AssessmentRepositoryImpl.java | 6 +- .../service/AssessmentServiceV2Impl.java | 16 +- 4 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/sunbird/assessment/RedisCacheMgr.java diff --git a/src/main/java/org/sunbird/assessment/RedisCacheMgr.java b/src/main/java/org/sunbird/assessment/RedisCacheMgr.java new file mode 100644 index 000000000..7328e93bc --- /dev/null +++ b/src/main/java/org/sunbird/assessment/RedisCacheMgr.java @@ -0,0 +1,144 @@ +package org.sunbird.assessment; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.sunbird.common.util.CbExtServerProperties; +import org.sunbird.common.util.Constants; +import org.sunbird.core.logger.CbExtLogger; + +@Component +public class RedisCacheMgr { + + private static final int cache_ttl = 84600; + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + CbExtServerProperties cbExtServerProperties; + + private CbExtLogger logger = new CbExtLogger(getClass().getName()); + + public void putCache(String key, Object object) { + try { + int ttl = cache_ttl; + if (!StringUtils.isEmpty(cbExtServerProperties.getRedisTimeout())) { + ttl = Integer.parseInt(cbExtServerProperties.getRedisTimeout()); + } + redisTemplate.opsForValue().set(Constants.REDIS_COMMON_KEY + key, object); + redisTemplate.expire(Constants.REDIS_COMMON_KEY + key, ttl, TimeUnit.SECONDS); + logger.info("Cache_key_value " + Constants.REDIS_COMMON_KEY + key + " is saved in redis"); + } catch (Exception e) { + logger.error(e); + } + } + + public boolean deleteKeyByName(String key) { + try { + key = key.toUpperCase(); + redisTemplate.delete(Constants.REDIS_COMMON_KEY + key); + logger.info("Cache_key_value " + Constants.REDIS_COMMON_KEY + key + " is deleted from redis"); + return true; + } catch (Exception e) { + logger.error(e); + return false; + } + } + + public boolean deleteAllKey() { + try { + String keyPattern = Constants.REDIS_COMMON_KEY + "*"; + Set keys = redisTemplate.keys(keyPattern); + for (String key : keys) { + redisTemplate.delete(key); + } + logger.info("All Keys starts with " + Constants.REDIS_COMMON_KEY + " is deleted from redis"); + return true; + } catch (Exception e) { + logger.error(e); + return false; + } + } + + public Object getCache(String key) { + try { + return redisTemplate.opsForValue().get(Constants.REDIS_COMMON_KEY + key); + } catch (Exception e) { + logger.error(e); + return null; + } + } + + public List mget(List fields) { + try { + List ls = new ArrayList<>(); + for (int i = 0; i < fields.size(); i++) { + ls.add(Constants.REDIS_COMMON_KEY + Constants.QUESTION_ID + fields.get(i)); + } + Collection questionIdList = ls; + return redisTemplate.opsForValue().multiGet(questionIdList); + } catch (Exception e) { + logger.error(e); + } + return null; + } + + public boolean deleteCache() { + try { + String keyPattern = "*"; + Set keys = redisTemplate.keys(keyPattern); + if (!keys.isEmpty()) { + for (String key : keys) { + + redisTemplate.delete(key); + } + logger.info("All Keys in Redis Cache is Deleted"); + return true; + } else { + return false; + } + } catch (Exception e) { + logger.error(e); + return false; + } + } + + public Set getAllKeys() { + Set keys = null; + try { + String keyPattern = "*"; + keys = redisTemplate.keys(keyPattern); + + } catch (Exception e) { + logger.error(e); + return Collections.emptySet(); + } + return keys; + } + + public List> getAllKeysAndValues() { + List> result = new ArrayList>(); + try { + String keyPattern = "*"; + Map res = new HashMap<>(); + Set keys = redisTemplate.keys(keyPattern); + if (!keys.isEmpty()) { + for (String key : keys) { + Object entries; + entries = redisTemplate.opsForValue().get(key); + res.put(key, entries); + } + result.add(res); + } + } catch (Exception e) { + logger.error(e); + return Collections.emptyList(); + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java b/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java index 73ed6d383..d89784878 100644 --- a/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java +++ b/src/main/java/org/sunbird/assessment/repo/AssessmentRepository.java @@ -56,5 +56,5 @@ boolean addUserAssesmentDataToDB(String userId, String assessmentId, Timestamp s Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdentifier, Map submitAssessmentRequest, Map submitAssessmentResponse, String status, - Date startTime, Date submitTime); + Date startTime); } diff --git a/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java b/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java index fb14c7905..66f75fc49 100644 --- a/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java +++ b/src/main/java/org/sunbird/assessment/repo/AssessmentRepositoryImpl.java @@ -161,7 +161,7 @@ public List> fetchUserAssessmentDataFromDB(String userId, St @Override public Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdentifier, Map submitAssessmentRequest, Map submitAssessmentResponse, String status, - Date startTime, Date submitTime) { + Date startTime) { Map compositeKeys = new HashMap<>(); compositeKeys.put(Constants.USER_ID, userId); compositeKeys.put(Constants.ASSESSMENT_ID_KEY, assessmentIdentifier); @@ -176,10 +176,6 @@ public Boolean updateUserAssesmentDataToDB(String userId, String assessmentIdent if (!status.isEmpty()) { fieldsToBeUpdated.put(Constants.STATUS, status); } - if (submitTime!=null) - { - fieldsToBeUpdated.put(Constants.SUBMIT_TIME, submitTime); - } cassandraOperation.updateRecord(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_USER_ASSESSMENT_DATA, fieldsToBeUpdated, compositeKeys); return true; diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java index 982f79eeb..0128fab82 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java @@ -687,16 +687,16 @@ public SBApiResponse retakeAssessment(String assessmentIdentifier, String token) List> existingDataList = assessmentRepository.fetchUserAssessmentDataFromDB(userId, assessmentIdentifier); if (!existingDataList.isEmpty()) { - Date assessmentEndTime = (!existingDataList.isEmpty()) + Date assessmentStartTime = (!existingDataList.isEmpty()) ? (Date) existingDataList.get(0).get(Constants.END_TIME) : null; - if (assessmentEndTime == null) { + if (assessmentStartTime == null) { errMsg = Constants.READ_ASSESSMENT_START_TIME_FAILED; } else { Map assessmentAllDetail = new HashMap<>(); errMsg = fetchReadHierarchyDetails(assessmentAllDetail, token, assessmentIdentifier); if (errMsg.isEmpty() && (assessmentAllDetail.get(Constants.RETAKE_ASSESSMENT_DURATION)) != null) { - long time = calculateAssessmentRetakeTime((int) assessmentAllDetail.get(Constants.RETAKE_ASSESSMENT_DURATION), assessmentEndTime); + long time = calculateAssessmentRetakeTime((int) assessmentAllDetail.get(Constants.RETAKE_ASSESSMENT_DURATION), assessmentStartTime); if (time > 0) errMsg = "You can retake this assessment after " + time + " seconds"; } @@ -717,13 +717,15 @@ public SBApiResponse retakeAssessment(String assessmentIdentifier, String token) return response; } - private long calculateAssessmentRetakeTime(int retakeAssessmentDuration, Date assessmentEndTime) { + private long calculateAssessmentRetakeTime(int retakeAssessmentDuration, Date assessmentStartTime) { Calendar retakeAssessmentTime = Calendar.getInstance(); - retakeAssessmentTime.setTimeInMillis(new Timestamp(assessmentEndTime.getTime()).getTime()); - retakeAssessmentTime.add(Calendar.MINUTE, + retakeAssessmentTime.setTimeInMillis(new Timestamp(assessmentStartTime.getTime()).getTime()); + retakeAssessmentTime.add(Calendar.SECOND, retakeAssessmentDuration); Calendar now = Calendar.getInstance(); - if (now.getTime().compareTo(retakeAssessmentTime.getTime())<0) { + Date time = now.getTime(); + Date time1 = retakeAssessmentTime.getTime(); + if (now.compareTo(retakeAssessmentTime)<0) { return TimeUnit.MILLISECONDS.toSeconds(Math.abs(retakeAssessmentTime.getTimeInMillis() - now.getTimeInMillis())); } return 0; From acc60596b2166f700df64dee6ab925c01c34cfc6 Mon Sep 17 00:00:00 2001 From: Juhi Date: Thu, 15 Dec 2022 22:54:38 +0530 Subject: [PATCH 13/13] changes --- pom.xml | 20 +++++++- .../org/sunbird/assessment/RedisCacheMgr.java | 46 ++++++++++--------- .../service/AssessmentServiceV2Impl.java | 38 +++++++++------ .../common/util/CbExtServerProperties.java | 11 ----- .../org/sunbird/core/config/RedisConfig.java | 42 +++++++++++++++++ 5 files changed, 109 insertions(+), 48 deletions(-) create mode 100644 src/main/java/org/sunbird/core/config/RedisConfig.java diff --git a/pom.xml b/pom.xml index a7a46a47b..de2fd322a 100644 --- a/pom.xml +++ b/pom.xml @@ -159,8 +159,26 @@ keycloak-admin-client 18.0.0 + + org.springframework.boot + spring-boot-starter-data-redis + + + io.lettuce + lettuce-core + + + + + redis.clients + jedis + - + + org.apache.commons + commons-pool2 + 2.11.1 + diff --git a/src/main/java/org/sunbird/assessment/RedisCacheMgr.java b/src/main/java/org/sunbird/assessment/RedisCacheMgr.java index 7328e93bc..35dc77dcc 100644 --- a/src/main/java/org/sunbird/assessment/RedisCacheMgr.java +++ b/src/main/java/org/sunbird/assessment/RedisCacheMgr.java @@ -1,37 +1,42 @@ package org.sunbird.assessment; import java.util.*; -import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.sunbird.common.util.CbExtServerProperties; import org.sunbird.common.util.Constants; import org.sunbird.core.logger.CbExtLogger; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; @Component public class RedisCacheMgr { private static final int cache_ttl = 84600; - @Autowired - private RedisTemplate redisTemplate; + private JedisPool jedisPool; @Autowired CbExtServerProperties cbExtServerProperties; private CbExtLogger logger = new CbExtLogger(getClass().getName()); - public void putCache(String key, Object object) { + public Jedis getJedis() { + try (Jedis jedis = jedisPool.getResource()) { + return jedis; + } + } + + public void putCache(String key, String object) { try { int ttl = cache_ttl; if (!StringUtils.isEmpty(cbExtServerProperties.getRedisTimeout())) { ttl = Integer.parseInt(cbExtServerProperties.getRedisTimeout()); } - redisTemplate.opsForValue().set(Constants.REDIS_COMMON_KEY + key, object); - redisTemplate.expire(Constants.REDIS_COMMON_KEY + key, ttl, TimeUnit.SECONDS); + getJedis().set(Constants.REDIS_COMMON_KEY + key, object); + getJedis().expire(Constants.REDIS_COMMON_KEY + key, ttl); logger.info("Cache_key_value " + Constants.REDIS_COMMON_KEY + key + " is saved in redis"); } catch (Exception e) { logger.error(e); @@ -41,7 +46,7 @@ public void putCache(String key, Object object) { public boolean deleteKeyByName(String key) { try { key = key.toUpperCase(); - redisTemplate.delete(Constants.REDIS_COMMON_KEY + key); + getJedis().del(Constants.REDIS_COMMON_KEY + key); logger.info("Cache_key_value " + Constants.REDIS_COMMON_KEY + key + " is deleted from redis"); return true; } catch (Exception e) { @@ -53,9 +58,9 @@ public boolean deleteKeyByName(String key) { public boolean deleteAllKey() { try { String keyPattern = Constants.REDIS_COMMON_KEY + "*"; - Set keys = redisTemplate.keys(keyPattern); + Set keys = getJedis().keys(keyPattern); for (String key : keys) { - redisTemplate.delete(key); + getJedis().del(key); } logger.info("All Keys starts with " + Constants.REDIS_COMMON_KEY + " is deleted from redis"); return true; @@ -65,23 +70,23 @@ public boolean deleteAllKey() { } } - public Object getCache(String key) { + public String getCache(String key) { try { - return redisTemplate.opsForValue().get(Constants.REDIS_COMMON_KEY + key); + return getJedis().get(Constants.REDIS_COMMON_KEY + key); } catch (Exception e) { logger.error(e); return null; } } - public List mget(List fields) { + public List mget(List fields) { try { List ls = new ArrayList<>(); for (int i = 0; i < fields.size(); i++) { ls.add(Constants.REDIS_COMMON_KEY + Constants.QUESTION_ID + fields.get(i)); } - Collection questionIdList = ls; - return redisTemplate.opsForValue().multiGet(questionIdList); + String[] keysForRedis = ls.toArray(new String[ls.size()]); + return getJedis().mget(keysForRedis); } catch (Exception e) { logger.error(e); } @@ -91,11 +96,10 @@ public List mget(List fields) { public boolean deleteCache() { try { String keyPattern = "*"; - Set keys = redisTemplate.keys(keyPattern); + Set keys = getJedis().keys(keyPattern); if (!keys.isEmpty()) { for (String key : keys) { - - redisTemplate.delete(key); + getJedis().del(key); } logger.info("All Keys in Redis Cache is Deleted"); return true; @@ -112,7 +116,7 @@ public Set getAllKeys() { Set keys = null; try { String keyPattern = "*"; - keys = redisTemplate.keys(keyPattern); + keys = getJedis().keys(keyPattern); } catch (Exception e) { logger.error(e); @@ -126,11 +130,11 @@ public List> getAllKeysAndValues() { try { String keyPattern = "*"; Map res = new HashMap<>(); - Set keys = redisTemplate.keys(keyPattern); + Set keys = getJedis().keys(keyPattern); if (!keys.isEmpty()) { for (String key : keys) { Object entries; - entries = redisTemplate.opsForValue().get(key); + entries = getJedis().get(key); res.put(key, entries); } result.add(res); diff --git a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java index 0128fab82..ae3140074 100644 --- a/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java +++ b/src/main/java/org/sunbird/assessment/service/AssessmentServiceV2Impl.java @@ -4,16 +4,7 @@ import java.lang.reflect.Type; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -26,17 +17,22 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; +import org.sunbird.assessment.RedisCacheMgr; import org.sunbird.assessment.model.Competency; import org.sunbird.assessment.repo.AssessmentRepository; import org.sunbird.common.model.SBApiResponse; import org.sunbird.common.util.CbExtServerProperties; import org.sunbird.common.util.Constants; import org.sunbird.common.util.RequestInterceptor; +import org.sunbird.core.config.RedisConfig; import org.sunbird.core.producer.Producer; import com.beust.jcommander.internal.Lists; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; @Service @SuppressWarnings("unchecked") @@ -59,17 +55,27 @@ public class AssessmentServiceV2Impl implements AssessmentServiceV2 { @Autowired RequestInterceptor requestInterceptor; + @Autowired + RedisCacheMgr redisCacheMgr; + public SBApiResponse readAssessment(String assessmentIdentifier, String token) { logger.info("AssessmentServiceV2Impl::readAssessment... Started"); SBApiResponse response = createDefaultResponse(Constants.API_QUESTIONSET_HIERARCHY_GET); - String errMsg; + String errMsg = ""; try { String userId = validateAuthTokenAndFetchUserId(token); if (userId != null) { logger.info("readAssessment.. userId :" + userId); Map assessmentAllDetail = new HashMap<>(); - errMsg = fetchReadHierarchyDetails(assessmentAllDetail, token, assessmentIdentifier); - if (errMsg.isEmpty() && !((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) + String assessment = (String) redisCacheMgr.getCache(Constants.ASSESSMENT_ID + assessmentIdentifier); + if (assessment != null) { + assessmentAllDetail = Arrays.stream(assessment.split(",")) + .map(s -> s.split("=")) + .collect(Collectors.toMap(s -> s[0], s -> s[1])); + } else { + errMsg = fetchReadHierarchyDetails(assessmentAllDetail, token, assessmentIdentifier); + } + if (errMsg.isEmpty() && assessmentAllDetail != null && !((String) assessmentAllDetail.get(Constants.PRIMARY_CATEGORY)) .equalsIgnoreCase(Constants.PRACTICE_QUESTION_SET)) { logger.info("Fetched assessment Details... for : " + assessmentIdentifier); List> existingDataList = assessmentRepository @@ -176,6 +182,8 @@ private String fetchReadHierarchyDetails(Map assessmentAllDetail || !Constants.OK.equalsIgnoreCase((String) readHierarchyApiResponse.get(Constants.RESPONSE_CODE))) { return Constants.ASSESSMENT_HIERARCHY_READ_FAILED; } + redisCacheMgr.putCache(Constants.ASSESSMENT_ID + assessmentIdentifier, String.valueOf(((Map) readHierarchyApiResponse.get(Constants.RESULT)) + .get(Constants.QUESTION_SET))); assessmentAllDetail .putAll((Map) ((Map) readHierarchyApiResponse.get(Constants.RESULT)) .get(Constants.QUESTION_SET)); @@ -388,7 +396,7 @@ private void writeDataToDatabaseAndTriggerKafkaEvent(Map submitR : null; Boolean isAssessmentUpdatedToDB = assessmentRepository.updateUserAssesmentDataToDB(userId, (String) submitRequest.get(Constants.IDENTIFIER), submitRequest, result, Constants.SUBMITTED, - startTime, new Timestamp(new Date().getTime())); + startTime); if (Boolean.TRUE.equals(isAssessmentUpdatedToDB)) { Map kafkaResult = new HashMap<>(); kafkaResult.put(Constants.CONTENT_ID_KEY, submitRequest.get(Constants.IDENTIFIER)); @@ -725,7 +733,7 @@ private long calculateAssessmentRetakeTime(int retakeAssessmentDuration, Date as Calendar now = Calendar.getInstance(); Date time = now.getTime(); Date time1 = retakeAssessmentTime.getTime(); - if (now.compareTo(retakeAssessmentTime)<0) { + if (now.compareTo(retakeAssessmentTime) < 0) { return TimeUnit.MILLISECONDS.toSeconds(Math.abs(retakeAssessmentTime.getTimeInMillis() - now.getTimeInMillis())); } return 0; diff --git a/src/main/java/org/sunbird/common/util/CbExtServerProperties.java b/src/main/java/org/sunbird/common/util/CbExtServerProperties.java index aed16e882..90c21454f 100644 --- a/src/main/java/org/sunbird/common/util/CbExtServerProperties.java +++ b/src/main/java/org/sunbird/common/util/CbExtServerProperties.java @@ -194,9 +194,6 @@ public class CbExtServerProperties { @Value("${redis.host.name}") private String redisHostName; - @Value("${redis.port}") - private String redisPort; - @Value("${redis.timeout}") private String redisTimeout; @@ -766,14 +763,6 @@ public void setCourseBatchCreateEndpoint(String courseBatchCreateEndpoint) { this.courseBatchCreateEndpoint = courseBatchCreateEndpoint; } - public String getRedisPort() { - return redisPort; - } - - public void setRedisPort(String redisPort) { - this.redisPort = redisPort; - } - public String getRedisHostName() { return redisHostName; } diff --git a/src/main/java/org/sunbird/core/config/RedisConfig.java b/src/main/java/org/sunbird/core/config/RedisConfig.java new file mode 100644 index 000000000..a1b14e019 --- /dev/null +++ b/src/main/java/org/sunbird/core/config/RedisConfig.java @@ -0,0 +1,42 @@ +package org.sunbird.core.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.sunbird.common.util.CbExtServerProperties; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +import java.time.Duration; + +@Configuration +@EnableCaching +public class RedisConfig { + + @Autowired + CbExtServerProperties cbProperties; + + public JedisPoolConfig buildPoolConfig() { + final JedisPoolConfig poolConfig = new JedisPoolConfig(); + poolConfig.setMaxIdle(128); + poolConfig.setMaxTotal(3000); + poolConfig.setMinIdle(100); + poolConfig.setTestOnBorrow(true); + poolConfig.setTestOnReturn(true); + poolConfig.setTestWhileIdle(true); + poolConfig.setMinEvictableIdleTime(Duration.ofSeconds(120)); + poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(30)); + poolConfig.setNumTestsPerEvictionRun(3); + poolConfig.setBlockWhenExhausted(true); + return poolConfig; + } + + @Bean + public JedisPool jedisPool() + { + final JedisPoolConfig poolConfig = buildPoolConfig(); + JedisPool jedisPool = new JedisPool(poolConfig, cbProperties.getRedisHostName()); + return jedisPool; + } +}