From c0fec6f3f3ea1923673191754ef22323eb5f07b0 Mon Sep 17 00:00:00 2001 From: karthik-tarento <karthikeyan.rajendran@tarento.com> Date: Thu, 20 Jan 2022 14:52:54 +0530 Subject: [PATCH] Changes from previous releases --- .../main/java/org/sunbird/common/Common.java | 37 ++++ .../java/org/sunbird/keys/SunbirdKey.java | 4 +- .../actors/event/EventContentUtil.java | 90 ++++++++ .../actors/event/EventManagementActor.java | 93 ++++++++ .../actors/event/EventSetManagementActor.java | 145 +++++++++++++ .../java/org/sunbird/learner/util/Util.java | 1 + .../CourseBatchNotificationActor.java | 2 + .../enrolments/ContentConsumptionActor.scala | 20 +- .../enrolments/CourseEnrolmentActor.scala | 3 +- .../enrolments/EventConsumptionActor.scala | 106 ++++++++++ .../enrolments/EventSetEnrolmentActor.scala | 198 ++++++++++++++++++ .../CourseConsumptionActorTest.scala | 89 ++++++++ .../cassandraimpl/CassandraDACImpl.java | 2 +- .../common/models/util/ActorOperations.java | 6 +- .../sunbird/common/models/util/JsonKey.java | 5 +- .../common/responsecode/ResponseCode.java | 6 + .../common/responsecode/ResponseMessage.java | 6 +- .../resources/externalresource.properties | 1 + .../CourseEnrollmentController.java | 44 ++++ .../CourseEnrollmentRequestValidator.java | 15 +- .../CourseBatchRequestValidator.java | 3 +- .../EventSetEnrollmentController.java | 47 +++++ .../EventConsumptionController.java | 81 +++++++ .../eventmanagement/EventController.java | 39 ++++ .../eventmanagement/EventSetController.java | 53 +++++ .../validator/EventRequestValidator.java | 53 +++++ service/app/util/ACTOR_NAMES.java | 10 +- service/conf/application.conf | 24 +++ service/conf/routes | 18 +- 29 files changed, 1184 insertions(+), 17 deletions(-) create mode 100644 course-mw/course-actors-common/src/main/java/org/sunbird/common/Common.java create mode 100644 course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventContentUtil.java create mode 100644 course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventManagementActor.java create mode 100644 course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventSetManagementActor.java create mode 100644 course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventConsumptionActor.scala create mode 100644 course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventSetEnrolmentActor.scala create mode 100644 service/app/controllers/eventenrollment/EventSetEnrollmentController.java create mode 100644 service/app/controllers/eventmanagement/EventConsumptionController.java create mode 100644 service/app/controllers/eventmanagement/EventController.java create mode 100644 service/app/controllers/eventmanagement/EventSetController.java create mode 100644 service/app/controllers/eventmanagement/validator/EventRequestValidator.java diff --git a/course-mw/course-actors-common/src/main/java/org/sunbird/common/Common.java b/course-mw/course-actors-common/src/main/java/org/sunbird/common/Common.java new file mode 100644 index 000000000..40f0dd04e --- /dev/null +++ b/course-mw/course-actors-common/src/main/java/org/sunbird/common/Common.java @@ -0,0 +1,37 @@ +package org.sunbird.common; + +import org.sunbird.common.models.util.JsonKey; +import org.sunbird.common.request.Request; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; + +public class Common { + public static Map<String, String[]> getRequestHeadersInArray(Map<String, List<String>> requestHeaders) { + Map<String, String[]> requestHeadersArray = new HashMap(); + requestHeaders.entrySet().forEach(entry -> requestHeadersArray.put(entry.getKey(), entry.getValue().toArray(new String[0]))); + return requestHeadersArray; + } + + public static void handleFixedBatchIdRequest(Request request) { + String courseIdKey = request.getRequest().containsKey(JsonKey.COURSE_ID) + ? JsonKey.COURSE_ID + : (request.getRequest().containsKey(JsonKey.ENROLLABLE_ITEM_ID) + ? JsonKey.ENROLLABLE_ITEM_ID + : JsonKey.COLLECTION_ID); + String courseId = request.getRequest().getOrDefault(courseIdKey, "").toString(); + request.getRequest().put(JsonKey.COURSE_ID, courseId); + //Till we add a type, we will use fixed batch identifier to prefix to course to get the batchId + String fixedBatchId = request.getRequest().getOrDefault(JsonKey.FIXED_BATCH_ID, "").toString(); + String batchId = request.getRequest().getOrDefault(JsonKey.BATCH_ID, "").toString(); + if (!fixedBatchId.isEmpty()) { + batchId = formBatchIdForFixedBatchId(courseId, fixedBatchId); + } + request.getRequest().put(JsonKey.BATCH_ID, batchId); + } + + public static String formBatchIdForFixedBatchId(String courseId, String fixedBatchId) { + return fixedBatchId + "-" + courseId; + } +} diff --git a/course-mw/course-actors-common/src/main/java/org/sunbird/keys/SunbirdKey.java b/course-mw/course-actors-common/src/main/java/org/sunbird/keys/SunbirdKey.java index c5f88e44f..a5f0e2199 100644 --- a/course-mw/course-actors-common/src/main/java/org/sunbird/keys/SunbirdKey.java +++ b/course-mw/course-actors-common/src/main/java/org/sunbird/keys/SunbirdKey.java @@ -54,7 +54,9 @@ public final class SunbirdKey { public static final String ACTIVITY_TYPE = "activity_type"; public static final String USER_ID = "user_id"; public static final String RESPONSE = "response"; - + public static final String COLLECTION = "collection"; + public static final String EVENT_SET = "eventSet"; + public static final String OBJECT_TYPE = "objectType"; private SunbirdKey() {} } diff --git a/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventContentUtil.java b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventContentUtil.java new file mode 100644 index 000000000..e98ca0dbe --- /dev/null +++ b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventContentUtil.java @@ -0,0 +1,90 @@ +package org.sunbird.learner.actors.event; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mashape.unirest.http.HttpResponse; +import com.mashape.unirest.http.Unirest; +import com.mashape.unirest.http.exceptions.UnirestException; +import org.sunbird.common.models.response.Response; +import org.sunbird.common.models.util.JsonKey; +import org.sunbird.common.request.Request; +import org.sunbird.common.responsecode.ResponseCode; +import org.sunbird.keys.SunbirdKey; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.sunbird.common.models.util.JsonKey.EKSTEP_BASE_URL; +import static org.sunbird.common.models.util.ProjectUtil.getConfigValue; + +public class EventContentUtil { + + private static final ObjectMapper mapper = new ObjectMapper(); + + public static List<String> getChildEventIds(Request request, String eventSetId) throws JsonProcessingException, UnirestException { + Response response = getContent(request, JsonKey.IDENTIFIER, eventSetId, new HashMap<>(), "/eventset/v4/hierarchy/{identifier}"); + if (response != null && response.getResponseCode().getResponseCode() == ResponseCode.OK.getResponseCode()) { + Map<String, Object> result = (Map<String, Object>) response.getResult().getOrDefault(SunbirdKey.EVENT_SET, new HashMap<String, Object>()); + return (List<String>) result.getOrDefault("childNodes", new ArrayList<String>()); + } + else + return new ArrayList<>(); + } + + private static Response getContent(Request request, String pathId, String pathValue, Map<String, Object> queryMap, String uri) throws UnirestException, JsonProcessingException { + String requestUrl = getConfigValue(EKSTEP_BASE_URL) + uri; + Map<String, String> headers = new HashMap<>(); + headers.put(SunbirdKey.CONTENT_TYPE_HEADER, SunbirdKey.APPLICATION_JSON); + headers.put(SunbirdKey.X_CHANNEL_ID, request.getContext().getOrDefault(SunbirdKey.CHANNEL, "").toString()); + HttpResponse<String> httpResponse = Unirest.get(requestUrl).headers(headers).routeParam(pathId, pathValue).queryString(queryMap).asString(); + Response response = null; + if (null != httpResponse) { + response = mapper.readValue(httpResponse.getBody(), Response.class); + } + return response; + } + + public static Response postContent(Request request, String contentKey, String uri, Map<String, Object> contentMap, String pathId, String pathVal) throws UnirestException, JsonProcessingException { + String requestUrl = getConfigValue(EKSTEP_BASE_URL) + uri; + Map<String, String> headers = new HashMap<String, String>() {{ + put(SunbirdKey.CONTENT_TYPE_HEADER, SunbirdKey.APPLICATION_JSON); + put(SunbirdKey.X_CHANNEL_ID, (String) request.getContext().get(SunbirdKey.CHANNEL)); + }}; + Map<String, Object> requestMap = new HashMap<String, Object>() {{ + put(SunbirdKey.REQUEST, new HashMap<String, Object>() {{ + put(contentKey, contentMap); + }}); + }}; + + HttpResponse<String> updateResponse = + Unirest.patch(requestUrl) + .headers(headers) + .routeParam(pathId, pathVal) + .body(mapper.writeValueAsString(requestMap)) + .asString(); + + Response response = null; + if (null != updateResponse) response = mapper.readValue(updateResponse.getBody(), Response.class); + return response; + } + + public static Response deleteContent(Request request, String uri, String pathId, String pathVal) throws UnirestException, JsonProcessingException { + String requestUrl = getConfigValue(EKSTEP_BASE_URL) + uri; + Map<String, String> headers = new HashMap<String, String>() {{ + put(SunbirdKey.CONTENT_TYPE_HEADER, SunbirdKey.APPLICATION_JSON); + put(SunbirdKey.X_CHANNEL_ID, (String) request.getContext().get(SunbirdKey.CHANNEL)); + }}; + + HttpResponse<String> updateResponse = + Unirest.delete(requestUrl) + .headers(headers) + .routeParam(pathId, pathVal) + .asString(); + + Response response = null; + if (null != updateResponse) response = mapper.readValue(updateResponse.getBody(), Response.class); + return response; + } +} diff --git a/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventManagementActor.java b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventManagementActor.java new file mode 100644 index 000000000..901c36837 --- /dev/null +++ b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventManagementActor.java @@ -0,0 +1,93 @@ +package org.sunbird.learner.actors.event; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.sunbird.actor.base.BaseActor; +import org.sunbird.common.Common; +import org.sunbird.common.exception.ProjectCommonException; +import org.sunbird.common.models.response.Response; +import org.sunbird.common.models.util.JsonKey; +import org.sunbird.common.request.Request; +import org.sunbird.common.responsecode.ResponseCode; +import org.sunbird.keys.SunbirdKey; +import org.sunbird.learner.actors.coursebatch.service.UserCoursesService; + +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +public class EventManagementActor extends BaseActor { + + private final UserCoursesService userCoursesService = new UserCoursesService(); + + @Override + public void onReceive(Request request) throws Throwable { + String requestedOperation = request.getOperation(); + switch (requestedOperation) { + case "discardEvent": + discardEvent(request); + break; + default: + onReceiveUnsupportedOperation(requestedOperation); + break; + } + } + + private void discardEvent(Request request) throws Exception { + validateNoEnrollments(request); + String pathId = JsonKey.IDENTIFIER; + String pathVal = request.getRequest().getOrDefault(JsonKey.IDENTIFIER, "").toString(); + Response response = EventContentUtil.deleteContent(request, "/private/event/v4/discard/{identifier}", pathId, pathVal); + try { + if (response != null && response.getResponseCode().getResponseCode() == ResponseCode.OK.getResponseCode()) { + sender().tell(response, self()); + } else if (response != null) { + Map<String, Object> resultMap = + Optional.ofNullable(response.getResult()).orElse(new HashMap<>()); + String message = "Event discard failed "; + if (MapUtils.isNotEmpty(resultMap)) { + Object obj = Optional.ofNullable(resultMap.get(SunbirdKey.TB_MESSAGES)).orElse(""); + if (obj instanceof List) { + message += ((List<String>) obj).stream().collect(Collectors.joining(";")); + } else if (StringUtils.isNotEmpty(response.getParams().getErrmsg())) { + message += response.getParams().getErrmsg(); + } else { + message += String.valueOf(obj); + } + } + ProjectCommonException.throwClientErrorException( + ResponseCode.customServerError, + MessageFormat.format( + ResponseCode.customServerError.getErrorMessage(), message)); + } else { + ProjectCommonException.throwClientErrorException(ResponseCode.CLIENT_ERROR); + } + } catch (Exception ex) { + logger.error(request.getRequestContext(), "EventManagementActor:discardEvent : discard error ", ex); + if (ex instanceof ProjectCommonException) { + throw ex; + } else { + throw new ProjectCommonException( + ResponseCode.SERVER_ERROR.getErrorCode(), + ResponseCode.SERVER_ERROR.getErrorMessage(), + ResponseCode.SERVER_ERROR.getResponseCode()); + } + } + } + + private void validateNoEnrollments(Request request) { + String identifier = request.get(SunbirdKey.IDENTIFIER).toString(); + String fixedBatchId = request.get(JsonKey.FIXED_BATCH_ID).toString(); + String batchId = Common.formBatchIdForFixedBatchId(identifier, fixedBatchId); + List<String> participants = userCoursesService.getParticipantsList(batchId, true, request.getRequestContext()); + if (!participants.isEmpty()) { + ProjectCommonException.throwClientErrorException( + ResponseCode.cannotUpdateEventSetHavingEnrollments, + ResponseCode.cannotUpdateEventSetHavingEnrollments.getErrorMessage()); + } + } + +} diff --git a/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventSetManagementActor.java b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventSetManagementActor.java new file mode 100644 index 000000000..f6a6e8505 --- /dev/null +++ b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/actors/event/EventSetManagementActor.java @@ -0,0 +1,145 @@ +package org.sunbird.learner.actors.event; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.mashape.unirest.http.exceptions.UnirestException; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.sunbird.actor.base.BaseActor; +import org.sunbird.common.Common; +import org.sunbird.common.exception.ProjectCommonException; +import org.sunbird.common.models.response.Response; +import org.sunbird.common.models.util.JsonKey; +import org.sunbird.common.request.Request; +import org.sunbird.common.responsecode.ResponseCode; +import org.sunbird.keys.SunbirdKey; +import org.sunbird.learner.actors.coursebatch.service.UserCoursesService; + +import java.text.MessageFormat; +import java.util.*; +import java.util.stream.Collectors; + +public class EventSetManagementActor extends BaseActor { + private final UserCoursesService userCoursesService = new UserCoursesService(); + + @Override + public void onReceive(Request request) throws Throwable { + String requestedOperation = request.getOperation(); + switch (requestedOperation) { + case "updateEventSet": + updateEventSet(request); + break; + case "discardEventSet": + discardEventSet(request); + break; + default: + onReceiveUnsupportedOperation(requestedOperation); + break; + } + } + + private void updateEventSet(Request request) throws Exception { + validateNoEventEnrollments(request); + try { + Map<String, Object> contentMap = new HashMap<>((Map<String, Object>) request.get(SunbirdKey.EVENT_SET)); + String pathId = JsonKey.IDENTIFIER; + String pathVal = request.getRequest().getOrDefault(JsonKey.IDENTIFIER, "").toString(); + Response response = EventContentUtil.postContent(request, SunbirdKey.EVENT_SET, "/private/eventset/v4/update/{identifier}", contentMap, pathId, pathVal); + if (null != response) { + if (response.getResponseCode().getResponseCode() == ResponseCode.OK.getResponseCode()) { + sender().tell(response, self()); + } else { + String message = formErrorDetailsMessage(response, "EventSet updation failed "); + ProjectCommonException.throwClientErrorException( + ResponseCode.customServerError, + MessageFormat.format( + ResponseCode.customServerError.getErrorMessage(), message)); + } + } else { + ProjectCommonException.throwClientErrorException(ResponseCode.CLIENT_ERROR); + } + } catch (Exception ex) { + logger.error(request.getRequestContext(), "EventSetManagementActor:updateEventSet : update error ", ex); + if (ex instanceof ProjectCommonException) { + throw ex; + } else { + throw new ProjectCommonException( + ResponseCode.SERVER_ERROR.getErrorCode(), + ResponseCode.SERVER_ERROR.getErrorMessage(), + ResponseCode.SERVER_ERROR.getResponseCode()); + } + } + } + + private String formErrorDetailsMessage(Response response, String message) { + Map<String, Object> resultMap = + Optional.ofNullable(response.getResult()).orElse(new HashMap<>()); + if (MapUtils.isNotEmpty(resultMap)) { + Object obj = Optional.ofNullable(resultMap.get(SunbirdKey.TB_MESSAGES)).orElse(""); + if (obj instanceof List) { + message += ((List<String>) obj).stream().collect(Collectors.joining(";")); + } else if (StringUtils.isNotEmpty(response.getParams().getErrmsg())) { + message += response.getParams().getErrmsg(); + } else { + message += String.valueOf(obj); + } + } + return message; + } + + private void discardEventSet(Request request) throws Exception { + validateNoEventEnrollments(request); + try { + String pathId = JsonKey.IDENTIFIER; + String pathVal = request.getRequest().getOrDefault(JsonKey.IDENTIFIER, "").toString(); + Response response = EventContentUtil.deleteContent(request, "/private/eventset/v4/discard/{identifier}", pathId, pathVal); + if (null != response) { + if (response.getResponseCode().getResponseCode() == ResponseCode.OK.getResponseCode()) { + sender().tell(response, self()); + } else { + String message = formErrorDetailsMessage(response, "EventSet discard failed "); + ProjectCommonException.throwClientErrorException( + ResponseCode.customServerError, + MessageFormat.format( + ResponseCode.customServerError.getErrorMessage(), message)); + } + } else { + ProjectCommonException.throwClientErrorException(ResponseCode.CLIENT_ERROR); + } + } catch (Exception ex) { + logger.error(request.getRequestContext(), "EventSetManagementActor:discardEventSet : discard error ", ex); + if (ex instanceof ProjectCommonException) { + throw ex; + } else { + throw new ProjectCommonException( + ResponseCode.SERVER_ERROR.getErrorCode(), + ResponseCode.SERVER_ERROR.getErrorMessage(), + ResponseCode.SERVER_ERROR.getResponseCode()); + } + } + } + + private void validateNoEventEnrollments(Request request) { + String identifier = request.get(SunbirdKey.IDENTIFIER).toString(); + String fixedBatchId = request.get(JsonKey.FIXED_BATCH_ID).toString(); + List<String> participants = new ArrayList<>(); + try { + List<String> eventsIds = EventContentUtil.getChildEventIds(request, identifier); + participants = eventsIds.stream().map(childId -> { + String childBatchId = Common.formBatchIdForFixedBatchId(childId, fixedBatchId); + return userCoursesService.getParticipantsList(childBatchId, true, request.getRequestContext()); + }) + .filter(Objects::nonNull) + .flatMap(List::stream) + .collect(Collectors.toList()); + } catch (UnirestException | JsonProcessingException e) { + ProjectCommonException.throwServerErrorException( + ResponseCode.SERVER_ERROR, + e.getMessage()); + } + if (!participants.isEmpty()) { + ProjectCommonException.throwClientErrorException( + ResponseCode.cannotUpdateEventSetHavingEnrollments, + ResponseCode.cannotUpdateEventSetHavingEnrollments.getErrorMessage()); + } + } +} diff --git a/course-mw/course-actors-common/src/main/java/org/sunbird/learner/util/Util.java b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/util/Util.java index 168111449..2f0790913 100644 --- a/course-mw/course-actors-common/src/main/java/org/sunbird/learner/util/Util.java +++ b/course-mw/course-actors-common/src/main/java/org/sunbird/learner/util/Util.java @@ -51,6 +51,7 @@ private static void initializeDBProperty() { JsonKey.LEARNER_CONTENT_DB, getDbInfoObject(COURSE_KEY_SPACE_NAME, "user_content_consumption")); dbInfoMap.put( JsonKey.COURSE_MANAGEMENT_DB, getDbInfoObject(KEY_SPACE_NAME, "course_management")); + dbInfoMap.put(JsonKey.COURSE_USER_ENROLMENTS_DB, getDbInfoObject(COURSE_KEY_SPACE_NAME, "user_enrolments")); dbInfoMap.put(JsonKey.PAGE_MGMT_DB, getDbInfoObject(KEY_SPACE_NAME, "page_management")); dbInfoMap.put(JsonKey.PAGE_SECTION_DB, getDbInfoObject(KEY_SPACE_NAME, "page_section")); dbInfoMap.put(JsonKey.SECTION_MGMT_DB, getDbInfoObject(KEY_SPACE_NAME, "page_section")); diff --git a/course-mw/course-actors/src/main/java/org/sunbird/learner/actors/coursebatch/CourseBatchNotificationActor.java b/course-mw/course-actors/src/main/java/org/sunbird/learner/actors/coursebatch/CourseBatchNotificationActor.java index 2323b0ade..db79c9324 100644 --- a/course-mw/course-actors/src/main/java/org/sunbird/learner/actors/coursebatch/CourseBatchNotificationActor.java +++ b/course-mw/course-actors/src/main/java/org/sunbird/learner/actors/coursebatch/CourseBatchNotificationActor.java @@ -31,6 +31,8 @@ public class CourseBatchNotificationActor extends BaseActor { .getProperty(JsonKey.SUNBIRD_COURSE_BATCH_NOTIFICATION_SIGNATURE); private static String baseUrl = PropertiesCache.getInstance().getProperty(JsonKey.SUNBIRD_WEB_URL); + private static String courseBatchPath = + PropertiesCache.getInstance().getProperty(JsonKey.COURSE_BATCH_PATH); private UserOrgService userOrgService = UserOrgServiceImpl.getInstance(); @Override diff --git a/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/ContentConsumptionActor.scala b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/ContentConsumptionActor.scala index d8fb74753..8d2a9e433 100644 --- a/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/ContentConsumptionActor.scala +++ b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/ContentConsumptionActor.scala @@ -36,6 +36,10 @@ class ContentConsumptionActor @Inject() extends BaseEnrolmentActor { private val enrolmentDBInfo = Util.dbInfoMap.get(JsonKey.LEARNER_COURSE_DB) val dateFormatter = ProjectUtil.getDateFormatter + //Added default fields in env varible: + val defaultFields: Set[String] = if(ProjectUtil.getConfigValue("content.default.fields")!=null) ProjectUtil.getConfigValue("content.default.fields").split(",").toSet else Set[String]("contentid","userid","batchid","courseid","completedcount","completionpercentage","lastcompletedtime","status","viewcount") + val jsonFields = Set[String]("progressdetails") + override def onReceive(request: Request): Unit = { Util.initializeContext(request, TelemetryEnvKey.BATCH, this.getClass.getName) @@ -170,7 +174,7 @@ class ContentConsumptionActor @Inject() extends BaseEnrolmentActor { val courseId = if (entry._2.head.containsKey(JsonKey.COURSE_ID)) entry._2.head.getOrDefault(JsonKey.COURSE_ID, "").asInstanceOf[String] else entry._2.head.getOrDefault(JsonKey.COLLECTION_ID, "").asInstanceOf[String] if(entry._2.head.containsKey(JsonKey.COLLECTION_ID)) entry._2.head.remove(JsonKey.COLLECTION_ID) val contentIds = entry._2.map(e => e.getOrDefault(JsonKey.CONTENT_ID, "").asInstanceOf[String]).distinct.asJava - val existingContents = getContentsConsumption(userId, courseId, contentIds, batchId, requestContext).groupBy(x => x.get("contentId").asInstanceOf[String]).map(e => e._1 -> e._2.toList.head).toMap + val existingContents = getContentsConsumption(userId, courseId, contentIds, batchId, defaultFields, requestContext).groupBy(x => x.get("contentId").asInstanceOf[String]).map(e => e._1 -> e._2.toList.head).toMap val contents:List[java.util.Map[String, AnyRef]] = entry._2.toList.map(inputContent => { val existingContent = existingContents.getOrElse(inputContent.get("contentId").asInstanceOf[String], new java.util.HashMap[String, AnyRef]) CassandraUtil.changeCassandraColumnMapping(processContentConsumption(inputContent, existingContent, userId)) @@ -235,7 +239,7 @@ class ContentConsumptionActor @Inject() extends BaseEnrolmentActor { } } - def getContentsConsumption(userId: String, courseId : String, contentIds: java.util.List[String], batchId: String, requestContext: RequestContext):java.util.List[java.util.Map[String, AnyRef]] = { + def getContentsConsumption(userId: String, courseId : String, contentIds: java.util.List[String], batchId: String, fields: java.util.Set[String], requestContext: RequestContext):java.util.List[java.util.Map[String, AnyRef]] = { val filters = new java.util.HashMap[String, AnyRef]() {{ put("userid", userId) put("courseid", courseId) @@ -243,7 +247,7 @@ class ContentConsumptionActor @Inject() extends BaseEnrolmentActor { if(CollectionUtils.isNotEmpty(contentIds)) put("contentid", contentIds) }} - val response = cassandraOperation.getRecords(requestContext, consumptionDBInfo.getKeySpace, consumptionDBInfo.getTableName, filters, null) + val response = cassandraOperation.getRecords(requestContext, consumptionDBInfo.getKeySpace, consumptionDBInfo.getTableName, filters, fields.toList) response.getResult.getOrDefault(JsonKey.RESPONSE, new java.util.ArrayList[java.util.Map[String, AnyRef]]).asInstanceOf[java.util.List[java.util.Map[String, AnyRef]]] } @@ -251,6 +255,9 @@ class ContentConsumptionActor @Inject() extends BaseEnrolmentActor { val inputStatus = inputContent.getOrDefault(JsonKey.STATUS, 0.asInstanceOf[AnyRef]).asInstanceOf[Number].intValue() val updatedContent = new java.util.HashMap[String, AnyRef]() updatedContent.putAll(inputContent) + val parsedMap = new java.util.HashMap[String, AnyRef]() + jsonFields.flatMap(f => if(inputContent.containsKey(f)) Some(parsedMap.put(f,mapper.writeValueAsString(inputContent.get(f)))) else None) + updatedContent.putAll(parsedMap) val inputCompletedTime = parseDate(inputContent.getOrDefault(JsonKey.LAST_COMPLETED_TIME, "").asInstanceOf[String]) val inputAccessTime = parseDate(inputContent.getOrDefault(JsonKey.LAST_ACCESS_TIME, "").asInstanceOf[String]) if(MapUtils.isNotEmpty(existingContent)) { @@ -357,12 +364,17 @@ class ContentConsumptionActor @Inject() extends BaseEnrolmentActor { val courseId = request.get(JsonKey.COURSE_ID).asInstanceOf[String] val contentIds = request.getRequest.getOrDefault(JsonKey.CONTENT_IDS, new java.util.ArrayList[String]()).asInstanceOf[java.util.List[String]] val fields = request.getRequest.getOrDefault(JsonKey.FIELDS, new java.util.ArrayList[String](){{ add(JsonKey.PROGRESS) }}).asInstanceOf[java.util.List[String]] - val contentsConsumed = getContentsConsumption(userId, courseId, contentIds, batchId, request.getRequestContext) + val finalFields = defaultFields ++ fields + val contentsConsumed = getContentsConsumption(userId, courseId, contentIds, batchId, finalFields.asJava, request.getRequestContext) val response = new Response if(CollectionUtils.isNotEmpty(contentsConsumed)) { val filteredContents = contentsConsumed.map(m => { ProjectUtil.removeUnwantedFields(m, JsonKey.DATE_TIME, JsonKey.USER_ID, JsonKey.ADDED_BY, JsonKey.LAST_UPDATED_TIME, JsonKey.OLD_LAST_ACCESS_TIME, JsonKey.OLD_LAST_UPDATED_TIME, JsonKey.OLD_LAST_COMPLETED_TIME) m.put(JsonKey.COLLECTION_ID, m.getOrDefault(JsonKey.COURSE_ID, "")) + jsonFields.foreach(field => + if(fields.contains(field)) + m.put(field, m.getOrDefault(field,"")) + ) val formattedMap = JsonUtil.convertWithDateFormat(m, classOf[util.Map[String, Object]], dateFormatter) if (fields.contains(JsonKey.ASSESSMENT_SCORE)) formattedMap.putAll(mapAsJavaMap(Map(JsonKey.ASSESSMENT_SCORE -> getScore(userId, courseId, m.get("contentId").asInstanceOf[String], batchId, request.getRequestContext)))) diff --git a/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/CourseEnrolmentActor.scala b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/CourseEnrolmentActor.scala index 204c2f8ca..3994defad 100644 --- a/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/CourseEnrolmentActor.scala +++ b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/CourseEnrolmentActor.scala @@ -198,7 +198,8 @@ class CourseEnrolmentActor @Inject()(@Named("course-batch-notification-actor") c def validateEnrolment(batchData: CourseBatch, enrolmentData: UserCourses, isEnrol: Boolean): Unit = { if(null == batchData) ProjectCommonException.throwClientErrorException(ResponseCode.invalidCourseBatchId, ResponseCode.invalidCourseBatchId.getErrorMessage) - if(EnrolmentType.inviteOnly.getVal.equalsIgnoreCase(batchData.getEnrollmentType)) + if(!(EnrolmentType.inviteOnly.getVal.equalsIgnoreCase(batchData.getEnrollmentType) || + EnrolmentType.open.getVal.equalsIgnoreCase(batchData.getEnrollmentType))) ProjectCommonException.throwClientErrorException(ResponseCode.enrollmentTypeValidation, ResponseCode.enrollmentTypeValidation.getErrorMessage) if((2 == batchData.getStatus) || (null != batchData.getEndDate && LocalDateTime.now().isAfter(LocalDate.parse(DATE_FORMAT.format(batchData.getEndDate), DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX)))) diff --git a/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventConsumptionActor.scala b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventConsumptionActor.scala new file mode 100644 index 000000000..4ad29b5b2 --- /dev/null +++ b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventConsumptionActor.scala @@ -0,0 +1,106 @@ +package org.sunbird.enrolments + +import org.apache.commons.collections4.CollectionUtils +import org.apache.commons.lang3.StringUtils +import org.sunbird.common.models.response.Response +import org.sunbird.common.models.util._ +import org.sunbird.common.request.{Request, RequestContext} +import org.sunbird.common.responsecode.ResponseCode +import org.sunbird.helper.ServiceFactory +import org.sunbird.learner.util.Util + +import javax.inject.Inject +import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ + +class EventConsumptionActor @Inject() extends BaseEnrolmentActor { + private val cassandraOperation = ServiceFactory.getInstance + private val consumptionDBInfo = Util.dbInfoMap.get(JsonKey.LEARNER_CONTENT_DB) + private val dateFormatter = ProjectUtil.getDateFormatter + + override def onReceive(request: Request): Unit = { + request.getOperation match { + case "updateConsumption" => updateConsumption(request) + case "getConsumption" => getConsumption(request) + case _ => onReceiveUnsupportedOperation(request.getOperation) + } + } + + def updateConsumption(request: Request): Unit = { + val userId = request.get(JsonKey.USER_ID).asInstanceOf[String] + val batchId = request.get(JsonKey.BATCH_ID).asInstanceOf[String] + val courseId = request.get(JsonKey.COURSE_ID).asInstanceOf[String] + val existingConsumptionResult = getContentsConsumption(userId, courseId, batchId, request.getRequestContext) + val existingCompletedTime = if (existingConsumptionResult.isEmpty) null else parseDate(existingConsumptionResult.get(0).getOrDefault(JsonKey.LAST_COMPLETED_TIME, "").asInstanceOf[String]) + var status: Integer = request.getRequest.getOrDefault(JsonKey.STATUS, Integer.valueOf(1)).asInstanceOf[Integer] + var progress: Integer = request.getRequest.getOrDefault(JsonKey.PROGRESS, Integer.valueOf(1)).asInstanceOf[Integer] + var completedCount: Integer = 0; + if (status >= 2) { + status = 2 + completedCount = 1 + } + if (completedCount >= 1) { + completedCount = 1 + request.getRequest.put(JsonKey.LAST_COMPLETED_TIME, compareTime(existingCompletedTime, null)) + //note progress should still denote the actual progress, so not making it 100 percent explicitly + } + if (progress > 100) progress = 100 + request.getRequest.put(JsonKey.STATUS, status) + request.getRequest.put(JsonKey.PROGRESS, progress) + request.getRequest.put(JsonKey.COMPLETED_COUNT, completedCount) + request.getRequest.put(JsonKey.LAST_UPDATED_TIME, ProjectUtil.getFormattedDate) + cassandraOperation.upsertRecord(consumptionDBInfo.getKeySpace, consumptionDBInfo.getTableName, request.getRequest, request.getRequestContext) + val response = new Response() + response.setResponseCode(ResponseCode.success) + sender().tell(response, self) + } + + def parseDate(dateString: String) = { + if (StringUtils.isNotBlank(dateString) && !StringUtils.equalsIgnoreCase(JsonKey.NULL, dateString)) { + dateFormatter.parse(dateString) + } else null + } + + def compareTime(existingTime: java.util.Date, inputTime: java.util.Date): String = { + if (null == existingTime && null == inputTime) { + ProjectUtil.getFormattedDate + } else if (null == existingTime) dateFormatter.format(inputTime) + else if (null == inputTime) dateFormatter.format(existingTime) + else { + if (inputTime.after(existingTime)) dateFormatter.format(inputTime) + else dateFormatter.format(existingTime) + } + } + + def getConsumption(request: Request): Unit = { + val userId = request.get(JsonKey.USER_ID).asInstanceOf[String] + val batchId = request.get(JsonKey.BATCH_ID).asInstanceOf[String] + val courseId = request.get(JsonKey.COURSE_ID).asInstanceOf[String] + val contentsConsumed = getContentsConsumption(userId, courseId, batchId, request.getRequestContext) + val response = new Response + if (CollectionUtils.isNotEmpty(contentsConsumed)) { + val filteredContents = contentsConsumed.map(m => { + ProjectUtil.removeUnwantedFields(m, JsonKey.DATE_TIME, JsonKey.USER_ID, JsonKey.ADDED_BY, JsonKey.LAST_UPDATED_TIME) + m + }).asJava + response.put(JsonKey.RESPONSE, filteredContents) + } else { + response.put(JsonKey.RESPONSE, new java.util.ArrayList[AnyRef]()) + } + sender().tell(response, self) + } + + private def getContentsConsumption(userId: String, courseId: String, batchId: String, requestContext: RequestContext): java.util.List[java.util.Map[String, AnyRef]] = { + val filters = new java.util.HashMap[String, AnyRef]() { + { + put("userid", userId) + put("courseid", courseId) + put("batchid", batchId) + } + } + val response = cassandraOperation.getRecords(requestContext, consumptionDBInfo.getKeySpace, consumptionDBInfo.getTableName, filters, null) + response.getResult.getOrDefault(JsonKey.RESPONSE, new java.util.ArrayList[java.util.Map[String, AnyRef]]).asInstanceOf[java.util.List[java.util.Map[String, AnyRef]]] + } + +} + diff --git a/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventSetEnrolmentActor.scala b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventSetEnrolmentActor.scala new file mode 100644 index 000000000..6a6d6ffa4 --- /dev/null +++ b/course-mw/enrolment-actor/src/main/scala/org/sunbird/enrolments/EventSetEnrolmentActor.scala @@ -0,0 +1,198 @@ +package org.sunbird.enrolments + +import akka.actor.ActorRef +import com.fasterxml.jackson.databind.ObjectMapper +import org.sunbird.cache.util.RedisCacheUtil +import org.sunbird.common.Common +import org.sunbird.common.exception.ProjectCommonException +import org.sunbird.common.models.util.ProjectUtil.{EnrolmentType, ProgressStatus} +import org.sunbird.common.models.util._ +import org.sunbird.common.request.{Request, RequestContext} +import org.sunbird.common.responsecode.ResponseCode +import org.sunbird.learner.actors.coursebatch.dao.impl.{CourseBatchDaoImpl, UserCoursesDaoImpl} +import org.sunbird.learner.actors.coursebatch.dao.{CourseBatchDao, UserCoursesDao} +import org.sunbird.learner.actors.event.EventContentUtil +import org.sunbird.learner.util._ +import org.sunbird.models.course.batch.CourseBatch +import org.sunbird.models.user.courses.UserCourses +import org.sunbird.telemetry.util.TelemetryUtil +import java.sql.Timestamp +import java.text.SimpleDateFormat +import java.time.format.DateTimeFormatter +import java.time.{LocalDate, LocalDateTime, LocalTime} +import java.util +import java.util.Date + +import javax.inject.{Inject, Named} + +import scala.collection.JavaConversions._ +import scala.collection.JavaConverters._ + +class EventSetEnrolmentActor @Inject()(@Named("course-batch-notification-actor") courseBatchNotificationActorRef: ActorRef + )(implicit val cacheUtil: RedisCacheUtil) extends BaseEnrolmentActor { + + var courseBatchDao: CourseBatchDao = new CourseBatchDaoImpl() + var userCoursesDao: UserCoursesDao = new UserCoursesDaoImpl() + val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + private val DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd") + val mapper = new ObjectMapper + + override def preStart { + println("Starting EventSetEnrolmentActor") + } + + override def postStop { + cacheUtil.closePool() + println("EventSetEnrolmentActor stopped successfully") + } + + override def preRestart(reason: Throwable, message: Option[Any]) { + println(s"Restarting EventSetEnrolmentActor: $message") + reason.printStackTrace() + super.preRestart(reason, message) + } + + override def onReceive(request: Request): Unit = { + Util.initializeContext(request, TelemetryEnvKey.BATCH, null) + request.getOperation match { + case "enrol" => enroll(request) + case "unenrol" => unEnroll(request) + case _ => ProjectCommonException.throwClientErrorException(ResponseCode.invalidRequestData, + ResponseCode.invalidRequestData.getErrorMessage) + } + } + + def enroll(request: Request): Unit = { + val eventSetId: String = request.get(JsonKey.COURSE_ID).asInstanceOf[String] + val userId: String = request.get(JsonKey.USER_ID).asInstanceOf[String] + val fixedBatchId: String = request.get(JsonKey.FIXED_BATCH_ID).asInstanceOf[String] + val eventIds: util.List[String] = EventContentUtil.getChildEventIds(request, eventSetId) + eventIds.foreach(eventId => { + val childBatchId = Common.formBatchIdForFixedBatchId(eventId, fixedBatchId) + val batchData: CourseBatch = getBatch(request.getRequestContext, eventId, childBatchId, true) + val enrolmentData: UserCourses = userCoursesDao.read(request.getRequestContext, userId, eventId, childBatchId) + validateEnrolment(batchData, enrolmentData, true) + val data: util.Map[String, AnyRef] = createUserEnrolmentMap(userId, eventId, childBatchId, enrolmentData, request.getContext.getOrDefault(JsonKey.REQUEST_ID, "").asInstanceOf[String]) + upsertEnrollment(userId, eventId, childBatchId, data, (null == enrolmentData), request.getRequestContext) + generateTelemetryAudit(userId, eventSetId, childBatchId, data, "enrol", JsonKey.CREATE, request.getContext) + notifyUser(userId, batchData, JsonKey.ADD) + }) + cacheUtil.delete(getCacheKey(userId)) + sender().tell(successResponse(), self) + } + + def unEnroll(request: Request): Unit = { + val eventSetId: String = request.get(JsonKey.COURSE_ID).asInstanceOf[String] + val userId: String = request.get(JsonKey.USER_ID).asInstanceOf[String] + val fixedBatchId: String = request.get(JsonKey.FIXED_BATCH_ID).asInstanceOf[String] + val eventIds: util.List[String] = EventContentUtil.getChildEventIds(request, eventSetId) + + eventIds.foreach(eventId => { + val childBatchId = Common.formBatchIdForFixedBatchId(eventId, fixedBatchId) + val batchData: CourseBatch = getBatch(request.getRequestContext, eventId, childBatchId, true) + val enrolmentData: UserCourses = userCoursesDao.read(request.getRequestContext, userId, eventId, childBatchId) + validateEnrolment(batchData, enrolmentData, false) + val data: java.util.Map[String, AnyRef] = new java.util.HashMap[String, AnyRef]() { + { + put(JsonKey.ACTIVE, ProjectUtil.ActiveStatus.INACTIVE.getValue.asInstanceOf[AnyRef]) + } + } + upsertEnrollment(userId, eventId, childBatchId, data, false, request.getRequestContext) + generateTelemetryAudit(userId, eventId, childBatchId, data, "unenrol", JsonKey.UPDATE, request.getContext) + notifyUser(userId, batchData, JsonKey.REMOVE) + }) + logger.info(request.getRequestContext, "EventSetEnrolmentActor :: unEnroll :: Deleting redis for key " + getCacheKey(userId)) + cacheUtil.delete(getCacheKey(userId)) + sender().tell(successResponse(), self) + } + + def validateEnrolment(batchData: CourseBatch, enrolmentData: UserCourses, isEnrol: Boolean): Unit = { + if (null == batchData) ProjectCommonException.throwClientErrorException(ResponseCode.invalidCourseBatchId, ResponseCode.invalidCourseBatchId.getErrorMessage) + + if (EnrolmentType.inviteOnly.getVal.equalsIgnoreCase(batchData.getEnrollmentType)) + ProjectCommonException.throwClientErrorException(ResponseCode.enrollmentTypeValidation, ResponseCode.enrollmentTypeValidation.getErrorMessage) + + if ((2 == batchData.getStatus) || (null != batchData.getEndDate && LocalDateTime.now().isAfter(LocalDate.parse(DATE_FORMAT.format(batchData.getEndDate), dateTimeFormatter).atTime(LocalTime.MAX)))) + ProjectCommonException.throwClientErrorException(ResponseCode.courseBatchAlreadyCompleted, ResponseCode.courseBatchAlreadyCompleted.getErrorMessage) + + if (isEnrol && null != batchData.getEnrollmentEndDate && LocalDateTime.now().isAfter(LocalDate.parse(DATE_FORMAT.format(batchData.getEnrollmentEndDate), dateTimeFormatter).atTime(LocalTime.MAX))) + ProjectCommonException.throwClientErrorException(ResponseCode.courseBatchEnrollmentDateEnded, ResponseCode.courseBatchEnrollmentDateEnded.getErrorMessage) + + if (isEnrol && null != enrolmentData && enrolmentData.isActive) ProjectCommonException.throwClientErrorException(ResponseCode.userAlreadyEnrolledCourse, ResponseCode.userAlreadyEnrolledCourse.getErrorMessage) + if (!isEnrol && (null == enrolmentData || !enrolmentData.isActive)) ProjectCommonException.throwClientErrorException(ResponseCode.userNotEnrolledCourse, ResponseCode.userNotEnrolledCourse.getErrorMessage) + if (!isEnrol && ProjectUtil.ProgressStatus.COMPLETED.getValue == enrolmentData.getStatus) ProjectCommonException.throwClientErrorException(ResponseCode.courseBatchAlreadyCompleted, ResponseCode.courseBatchAlreadyCompleted.getErrorMessage) + } + + def upsertEnrollment(userId: String, courseId: String, batchId: String, data: java.util.Map[String, AnyRef], isNew: Boolean, requestContext: RequestContext): Unit = { + if (isNew) { + userCoursesDao.insertV2(requestContext, data) + } else { + userCoursesDao.updateV2(requestContext, userId, courseId, batchId, data) + } + } + + def createUserEnrolmentMap(userId: String, courseId: String, batchId: String, enrolmentData: UserCourses, requestedBy: String): java.util.Map[String, AnyRef] = + new java.util.HashMap[String, AnyRef]() { + { + put(JsonKey.USER_ID, userId) + put(JsonKey.COURSE_ID, courseId) + put(JsonKey.BATCH_ID, batchId) + put(JsonKey.ACTIVE, ProjectUtil.ActiveStatus.ACTIVE.getValue.asInstanceOf[AnyRef]) + if (null == enrolmentData) { + put(JsonKey.ADDED_BY, requestedBy) + put(JsonKey.COURSE_ENROLL_DATE, ProjectUtil.getFormattedDate) + put(JsonKey.STATUS, ProjectUtil.ProgressStatus.NOT_STARTED.getValue.asInstanceOf[AnyRef]) + put(JsonKey.DATE_TIME, new Timestamp(new Date().getTime)) + put(JsonKey.COURSE_PROGRESS, 0.asInstanceOf[AnyRef]) + } + } + } + + def notifyUser(userId: String, batchData: CourseBatch, operationType: String): Unit = { + val isNotifyUser = java.lang.Boolean.parseBoolean(PropertiesCache.getInstance().getProperty(JsonKey.SUNBIRD_COURSE_BATCH_NOTIFICATIONS_ENABLED)) + if (isNotifyUser) { + val request = new Request() + request.setOperation(ActorOperations.COURSE_BATCH_NOTIFICATION.getValue) + request.put(JsonKey.USER_ID, userId) + request.put(JsonKey.COURSE_BATCH, batchData) + request.put(JsonKey.OPERATION_TYPE, operationType) + courseBatchNotificationActorRef.tell(request, getSelf()) + } + } + + def generateTelemetryAudit(userId: String, courseId: String, batchId: String, data: java.util.Map[String, AnyRef], correlation: String, state: String, context: java.util.Map[String, AnyRef]): Unit = { + val contextMap = new java.util.HashMap[String, AnyRef]() + contextMap.putAll(context) + contextMap.put(JsonKey.ACTOR_ID, userId) + contextMap.put(JsonKey.ACTOR_TYPE, "User") + val targetedObject = TelemetryUtil.generateTargetObject(userId, JsonKey.USER, state, null) + targetedObject.put(JsonKey.ROLLUP, new java.util.HashMap[String, AnyRef]() { + { + put("l1", courseId) + } + }) + val correlationObject = new java.util.ArrayList[java.util.Map[String, AnyRef]]() + TelemetryUtil.generateCorrelatedObject(courseId, JsonKey.COURSE, correlation, correlationObject) + TelemetryUtil.generateCorrelatedObject(batchId, TelemetryEnvKey.BATCH, "user.batch", correlationObject) + val request: java.util.Map[String, AnyRef] = Map[String, AnyRef](JsonKey.USER_ID -> userId, JsonKey.COURSE_ID -> courseId, JsonKey.BATCH_ID -> batchId, JsonKey.COURSE_ENROLL_DATE -> data.get(JsonKey.COURSE_ENROLL_DATE), JsonKey.ACTIVE -> data.get(JsonKey.ACTIVE)).asJava + TelemetryUtil.telemetryProcessingCall(request, targetedObject, correlationObject, contextMap, "enrol") + } + + def getCacheKey(userId: String) = s"$userId:user-enrolments" + + private def getBatch(requestContext: RequestContext, courseId: String, batchId: String, isFixedBatch: Boolean) = { + if (isFixedBatch) getFixedBatch(batchId, courseId) else courseBatchDao.readById(courseId, batchId, requestContext) + } + + private def getFixedBatch(batchId: String, courseId: String): CourseBatch = { + val batch = new CourseBatch + batch.setBatchId(batchId) + batch.setCourseId(courseId) + batch.setEnrollmentType(EnrolmentType.open.name()) + batch.setStatus(ProgressStatus.NOT_STARTED.getValue) + val enrolmentDate = LocalDate.now().plusDays(2).format(dateTimeFormatter); + batch.setEnrollmentEndDate(DATE_FORMAT.parse(enrolmentDate)) + batch + } + +} diff --git a/course-mw/enrolment-actor/src/test/scala/org/sunbird/enrolments/CourseConsumptionActorTest.scala b/course-mw/enrolment-actor/src/test/scala/org/sunbird/enrolments/CourseConsumptionActorTest.scala index 023c21857..d271574bb 100644 --- a/course-mw/enrolment-actor/src/test/scala/org/sunbird/enrolments/CourseConsumptionActorTest.scala +++ b/course-mw/enrolment-actor/src/test/scala/org/sunbird/enrolments/CourseConsumptionActorTest.scala @@ -261,4 +261,93 @@ class CourseConsumptionActorTest extends FlatSpec with Matchers with MockFactory request } + "get Consumption with progressDetails" should "return response" in { + val cassandraOperation = mock[CassandraOperation] + + val progressResponse = new java.util.HashMap[String, AnyRef]() + progressResponse.put("key1", "val1") + progressResponse.put("key2", "val2") + + val response = new Response() + response.put("response", new java.util.ArrayList[java.util.Map[String, AnyRef]] {{ + add(new java.util.HashMap[String, AnyRef] {{ + put("userId", "user1") + put("courseId", "do_123") + put("batchId", "0123") + put("contentId", "do_456") + put("progressdetails", "{}") + put("progressDetails", progressResponse) + }}) + }}) + + ((requestContext: RequestContext, keyspace: _root_.scala.Predef.String, table: _root_.scala.Predef.String, filters: _root_.java.util.Map[_root_.scala.Predef.String, AnyRef], fields: _root_.java.util.List[_root_.scala.Predef.String]) => cassandraOperation.getRecords(requestContext, keyspace, table, filters, fields)).expects(*,*,*,*,*).returns(response) + val result = callActor(getStateReadRequestWithProgressField(), Props(new ContentConsumptionActor().setCassandraOperation(cassandraOperation, false))) + + result.getResult().get("response").toString.shouldEqual("[{progressDetails={key1=val1, key2=val2}, contentId=do_456, batchId=0123, courseId=do_123, collectionId=do_123, progressdetails={}}]") + assert(null!= result) + } + + def getStateReadRequestWithProgressField(): Request = { + val request = new Request + request.setOperation("getConsumption") + request.put("userId", "user1") + request.put("courseId", "do_123") + request.put("batchId", "0123") + request.put("contentId", "do_456") + request.put("fields", new java.util.ArrayList[String](){{ add("progressDetails")}}) + + request + } + + "update Consumption with progressDetails" should "return success on updating the progress" in { + val cassandraOperation = mock[CassandraOperation] + val esService = mock[ElasticSearchService] + val progressResponse = new java.util.HashMap[String, AnyRef]() + progressResponse.put("key1", "val1") + progressResponse.put("key2", "val2") + val response = new Response() + response.put("response", new java.util.ArrayList[java.util.Map[String, AnyRef]] {{ + add(new java.util.HashMap[String, AnyRef] {{ + put("userId", "user1") + put("courseId", "do_123") + put("batchId", "0123") + put("contentId", "do_456") + put("progressDetails", progressResponse) + + }}) + + }}) + (esService.search(_:RequestContext, _: SearchDTO, _: String)).expects(*,*,*).returns(concurrent.Future{validBatchData()}) + (cassandraOperation.getRecords(_:RequestContext, _: String, _: String, _: java.util.Map[String, AnyRef], _: java.util.List[String])).expects(*,*,*,*,*).returns(response) + (cassandraOperation.batchInsertLogged(_:RequestContext, _: String, _: String, _: java.util.List[java.util.Map[String, AnyRef]])).expects(*,*,*,*) + (cassandraOperation.updateRecordV2(_:RequestContext, _: String, _: String, _: java.util.Map[String, AnyRef], _: java.util.Map[String, AnyRef], _: Boolean)).expects(*,"sunbird_courses", "user_enrolments",*,*,true) + val result = callActor(getStateUpdateRequestWithProgress(), Props(new ContentConsumptionActor().setCassandraOperation(cassandraOperation, false).setEsService(esService))) + assert(null!= result) + } + + def getStateUpdateRequestWithProgress(): Request = { + val request = new Request + request.setOperation("updateConsumption") + request.put("userId", "user1") + request.put("requestedBy", "user1") + request.put("contents", new java.util.ArrayList[java.util.Map[String, AnyRef]] {{ + add(new java.util.HashMap[String, AnyRef] {{ + put("courseId", "do_123") + put("batchId", "0123") + put("contentId", "do_456") + put("status", 2.asInstanceOf[AnyRef]) + put("progressDetails",new java.util.HashMap()) + + }}) + add(new java.util.HashMap[String, AnyRef] {{ + put("courseId", "do_123") + put("batchId", "0123") + put("contentId", "do_789") + put("status", 2.asInstanceOf[AnyRef]) + put("progressDetails",new java.util.HashMap()) + + }}) + }}) + request + } } diff --git a/course-mw/sunbird-util/sunbird-cassandra-utils/src/main/java/org/sunbird/cassandraimpl/CassandraDACImpl.java b/course-mw/sunbird-util/sunbird-cassandra-utils/src/main/java/org/sunbird/cassandraimpl/CassandraDACImpl.java index 5f05e2076..ff69c1616 100644 --- a/course-mw/sunbird-util/sunbird-cassandra-utils/src/main/java/org/sunbird/cassandraimpl/CassandraDACImpl.java +++ b/course-mw/sunbird-util/sunbird-cassandra-utils/src/main/java/org/sunbird/cassandraimpl/CassandraDACImpl.java @@ -32,7 +32,7 @@ public Response getRecords( try { Select select; if (CollectionUtils.isNotEmpty(fields)) { - select = QueryBuilder.select((String[]) fields.toArray()).from(keySpace, table); + select = QueryBuilder.select(fields.toArray()).from(keySpace, table); } else { select = QueryBuilder.select().all().from(keySpace, table); } diff --git a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/ActorOperations.java b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/ActorOperations.java index fb79c71bd..d3d2391e0 100644 --- a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/ActorOperations.java +++ b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/ActorOperations.java @@ -172,7 +172,11 @@ public enum ActorOperations { GET_USER_FEED_BY_ID("getUserFeedById"), CREATE_USER_V3("createUserV3"), ONDEMAND_START_SCHEDULER("onDemandStartScheduler"), - GROUP_ACTIVITY_AGGREGATES("groupActivityAggregates"); + GROUP_ACTIVITY_AGGREGATES("groupActivityAggregates"), + UPDATE_EVENT_SET("updateEventSet"), + DELETE_EVENT_SET("deleteEventSet"), + DELETE_EVENT("deleteEvent"); + private String value; /** diff --git a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/JsonKey.java b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/JsonKey.java index d627f8d2c..fcb41176f 100644 --- a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/JsonKey.java +++ b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/models/util/JsonKey.java @@ -1065,7 +1065,10 @@ public final class JsonKey { public static final String USER_ID_KEY = "userid"; public static final String OLD_CREATED_DATE = "oldCreatedDate"; public static final String X_LOGGING_HEADERS = "X_LOGGING_HEADERS"; - + public static final String ENROLLABLE_ITEM_ID = "enrollableItemId"; + public static final String FIXED_BATCH_ID = "fixedBatchId"; + public static final String COURSE_USER_ENROLMENTS_DB = "course_user_enrolments_db"; + public static final String COURSE_BATCH_PATH = "sunbird_course_batch_path"; private JsonKey() {} } diff --git a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseCode.java b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseCode.java index 37063db23..09974387b 100644 --- a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseCode.java +++ b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseCode.java @@ -826,6 +826,12 @@ public enum ResponseCode { activityIdMismatch(ResponseMessage.Key.ACTIVITY_ID_MISSING, ResponseMessage.Message.ACTIVITY_ID_MISSING), activityTypeMismatch(ResponseMessage.Key.ACTIVITY_TYPE_MISSING, ResponseMessage.Message.ACTIVITY_TYPE_MISSING), erroCallGrooupAPI(ResponseMessage.Key.ERR_CALLING_GROUP_API, ResponseMessage.Message.ERR_CALLING_GROUP_API), + missingFixedBatchId( + ResponseMessage.Key.MISSING_FIXED_BATCH_ID, + ResponseMessage.Message.MISSING_FIXED_BATCH_ID), + cannotUpdateEventSetHavingEnrollments( + ResponseMessage.Key.CANNOT_UPDATE_EVENT_SET_HAVING_ENROLLMENTS, + ResponseMessage.Message.CANNOT_UPDATE_EVENT_SET_HAVING_ENROLLMENTS), OK(200), CLIENT_ERROR(400), diff --git a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseMessage.java b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseMessage.java index ff31f5f44..0331e505e 100644 --- a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseMessage.java +++ b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/java/org/sunbird/common/responsecode/ResponseMessage.java @@ -172,7 +172,7 @@ interface Message { String INVALID_CSV_FILE = "Please provide valid csv file."; String INVALID_COURSE_BATCH_ID = "Invalid course batch id "; String COURSE_BATCH_ID_MISSING = "Course batch Id required"; - String ENROLLMENT_TYPE_VALIDATION = "Enrollment type should be invite-only."; + String ENROLLMENT_TYPE_VALIDATION = "Invalid Enrollment type value."; String USER_NOT_BELONGS_TO_ANY_ORG = "User does not belongs to any org ."; String INVALID_OBJECT_TYPE = "Invalid Object Type."; String INVALID_PROGRESS_STATUS = @@ -457,6 +457,8 @@ interface Message { String ACTIVITY_ID_MISSING = "ActivityId is mandatory."; String ACTIVITY_TYPE_MISSING = "ActivityType is mandatory."; String ERR_CALLING_GROUP_API = "Error while calling group api."; + String MISSING_FIXED_BATCH_ID = "Missing Fixed Batch Id."; + String CANNOT_UPDATE_EVENT_SET_HAVING_ENROLLMENTS = "Cannot update event set having enrollments."; } interface Key { @@ -842,5 +844,7 @@ interface Key { String ACTIVITY_ID_MISSING = "ACTIVITY_ID_MISSING"; String ACTIVITY_TYPE_MISSING = "ACTIVITY_TYPE_MISSING"; String ERR_CALLING_GROUP_API = "ERR_CALLING_GROUOP_API"; + String MISSING_FIXED_BATCH_ID = "MISSING_FIXED_BATCH_ID"; + String CANNOT_UPDATE_EVENT_SET_HAVING_ENROLLMENTS = "CANNOT_UPDATE_EVENT_SET_HAVING_ENROLLMENTS"; } } diff --git a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/resources/externalresource.properties b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/resources/externalresource.properties index 4285b8f6d..5be5d08ce 100644 --- a/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/resources/externalresource.properties +++ b/course-mw/sunbird-util/sunbird-platform-core/common-util/src/main/resources/externalresource.properties @@ -203,3 +203,4 @@ kafka_assessment_topic= sunbird_msg_sender= sunbird_msg_91_auth= sunbird_api_mgr_base_url=https://dev.sunbirded.org/api +sunbird_course_batch_path=/app/toc/{courseId}/overview?batchId={batchId} diff --git a/service/app/controllers/courseenrollment/CourseEnrollmentController.java b/service/app/controllers/courseenrollment/CourseEnrollmentController.java index 16429a794..affffd4fa 100644 --- a/service/app/controllers/courseenrollment/CourseEnrollmentController.java +++ b/service/app/controllers/courseenrollment/CourseEnrollmentController.java @@ -9,6 +9,7 @@ import org.sunbird.common.request.Request; import play.mvc.Http; import play.mvc.Result; +import org.sunbird.common.Common; import javax.inject.Inject; import javax.inject.Named; @@ -188,4 +189,47 @@ public CompletionStage<Result> privateGetUserEnrolledCourses(Http.Request httpRe getAllRequestHeaders((httpRequest)), httpRequest); } + + public CompletionStage<Result> adminEnrollCourse(Http.Request httpRequest) { + return handleRequest(courseEnrolmentActor, "enrol", + httpRequest.body().asJson(), + (request) -> { + Request req = (Request) request; + Map<String, String[]> queryParams = new HashMap<>(httpRequest.queryString()); + String courseId = req.getRequest().containsKey(JsonKey.COURSE_ID) ? JsonKey.COURSE_ID : JsonKey.COLLECTION_ID; + req.getRequest().put(JsonKey.COURSE_ID, req.getRequest().get(courseId)); + validator.validateEnrollCourse(req); + return null; + }, + getAllRequestHeaders(httpRequest), + httpRequest); + } + + public CompletionStage<Result> adminUnenrollCourse(Http.Request httpRequest) { + return handleRequest( + courseEnrolmentActor, "unenrol", + httpRequest.body().asJson(), + (request) -> { + Request req = (Request) request; + Map<String, String[]> queryParams = new HashMap<>(httpRequest.queryString()); + String courseId = req.getRequest().containsKey(JsonKey.COURSE_ID) ? JsonKey.COURSE_ID : JsonKey.COLLECTION_ID; + req.getRequest().put(JsonKey.COURSE_ID, req.getRequest().get(courseId)); + validator.validateUnenrollCourse(req); + return null; + }, + getAllRequestHeaders(httpRequest), + httpRequest); + } + + public CompletionStage<Result> getParticipantsForFixedBatch(Http.Request httpRequest) { + return handleRequest(courseEnrolmentActor, "getParticipantsForFixedBatch", + httpRequest.body().asJson(), + (request) -> { + Common.handleFixedBatchIdRequest((Request) request); + new CourseEnrollmentRequestValidator().validateCourseParticipant((Request) request); + return null; + }, + getAllRequestHeaders(httpRequest), + httpRequest); + } } diff --git a/service/app/controllers/courseenrollment/validator/CourseEnrollmentRequestValidator.java b/service/app/controllers/courseenrollment/validator/CourseEnrollmentRequestValidator.java index 333d9c89d..70fcd430c 100644 --- a/service/app/controllers/courseenrollment/validator/CourseEnrollmentRequestValidator.java +++ b/service/app/controllers/courseenrollment/validator/CourseEnrollmentRequestValidator.java @@ -21,11 +21,11 @@ private void commonValidations(Request courseRequestDto) { validateParam( (String) courseRequestDto.getRequest().get(JsonKey.COURSE_ID), ResponseCode.mandatoryParamsMissing, - JsonKey.COURSE_ID+"/"+JsonKey.COLLECTION_ID); + JsonKey.COURSE_ID+"/"+JsonKey.ENROLLABLE_ITEM_ID+"/"+JsonKey.COLLECTION_ID); validateParam( (String) courseRequestDto.getRequest().get(JsonKey.BATCH_ID), ResponseCode.mandatoryParamsMissing, - JsonKey.BATCH_ID); + JsonKey.BATCH_ID+"/"+JsonKey.FIXED_BATCH_ID); validateParam( (String) courseRequestDto.getRequest().get(JsonKey.USER_ID), ResponseCode.mandatoryParamsMissing, @@ -49,4 +49,15 @@ public void validateUserEnrolledCourse(Request courseRequestDto) { ResponseCode.mandatoryParamsMissing, JsonKey.USER_ID); } + + public void validateCourseParticipant(Request courseRequestDto) { + validateParam( + (String) courseRequestDto.getRequest().get(JsonKey.COURSE_ID), + ResponseCode.mandatoryParamsMissing, + JsonKey.COURSE_ID+"/"+JsonKey.ENROLLABLE_ITEM_ID+"/"+JsonKey.COLLECTION_ID); + validateParam( + (String) courseRequestDto.getRequest().get(JsonKey.BATCH_ID), + ResponseCode.mandatoryParamsMissing, + JsonKey.BATCH_ID+"/"+JsonKey.FIXED_BATCH_ID); + } } diff --git a/service/app/controllers/coursemanagement/validator/CourseBatchRequestValidator.java b/service/app/controllers/coursemanagement/validator/CourseBatchRequestValidator.java index dca25ba97..ec747bf93 100644 --- a/service/app/controllers/coursemanagement/validator/CourseBatchRequestValidator.java +++ b/service/app/controllers/coursemanagement/validator/CourseBatchRequestValidator.java @@ -117,7 +117,8 @@ private void validateEnrolmentType(Request request) { ResponseCode.mandatoryParamsMissing, JsonKey.ENROLLMENT_TYPE); String enrolmentType = (String) request.getRequest().get(JsonKey.ENROLLMENT_TYPE); - if (!ProjectUtil.EnrolmentType.open.getVal().equalsIgnoreCase(enrolmentType)) { + if (!(ProjectUtil.EnrolmentType.open.getVal().equalsIgnoreCase(enrolmentType) + || ProjectUtil.EnrolmentType.inviteOnly.getVal().equalsIgnoreCase(enrolmentType))) { throw new ProjectCommonException( ResponseCode.invalidParameterValue.getErrorCode(), ResponseCode.invalidParameterValue.getErrorMessage(), diff --git a/service/app/controllers/eventenrollment/EventSetEnrollmentController.java b/service/app/controllers/eventenrollment/EventSetEnrollmentController.java new file mode 100644 index 000000000..62c1541e5 --- /dev/null +++ b/service/app/controllers/eventenrollment/EventSetEnrollmentController.java @@ -0,0 +1,47 @@ +package controllers.eventenrollment; + +import akka.actor.ActorRef; +import controllers.BaseController; +import controllers.courseenrollment.validator.CourseEnrollmentRequestValidator; +import org.sunbird.common.Common; +import org.sunbird.common.request.Request; +import play.mvc.Http; +import play.mvc.Result; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.concurrent.CompletionStage; + +public class EventSetEnrollmentController extends BaseController { + + @Inject + @Named("eventset-enrolment-actor") + private ActorRef actorRef; + + public CompletionStage<Result> enroll(Http.Request httpRequest) { + return handleRequest(actorRef, "enrol", + httpRequest.body().asJson(), + (request) -> { + Request req = (Request) request; + Common.handleFixedBatchIdRequest(req); + new CourseEnrollmentRequestValidator().validateEnrollCourse(req); + return null; + }, + getAllRequestHeaders(httpRequest), + httpRequest); + } + + public CompletionStage<Result> unenroll(Http.Request httpRequest) { + return handleRequest(actorRef, "unenrol", + httpRequest.body().asJson(), + (request) -> { + Request req = (Request) request; + Common.handleFixedBatchIdRequest(req); + new CourseEnrollmentRequestValidator().validateUnenrollCourse(req); + return null; + }, + getAllRequestHeaders(httpRequest), + httpRequest); + } + +} diff --git a/service/app/controllers/eventmanagement/EventConsumptionController.java b/service/app/controllers/eventmanagement/EventConsumptionController.java new file mode 100644 index 000000000..dade1f59e --- /dev/null +++ b/service/app/controllers/eventmanagement/EventConsumptionController.java @@ -0,0 +1,81 @@ +package controllers.eventmanagement; + +import akka.actor.ActorRef; +import com.fasterxml.jackson.databind.JsonNode; +import controllers.BaseController; +import org.sunbird.common.exception.ProjectCommonException; +import org.sunbird.common.models.util.JsonKey; +import org.sunbird.common.request.Request; +import org.sunbird.common.responsecode.ResponseCode; +import play.mvc.Http; +import play.mvc.Result; +import util.Attrs; +import org.sunbird.common.Common; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +public class EventConsumptionController extends BaseController { + + @Inject + @Named("event-consumption-actor") + private ActorRef actorRef; + + public CompletionStage<Result> getUserEventState(Http.Request httpRequest) { + try { + JsonNode requestJson = httpRequest.body().asJson(); + Request request = + createAndInitRequest("getConsumption", requestJson, httpRequest); + request = transformUserId(request); + Common.handleFixedBatchIdRequest(request); + validateAndSetRequest(request); + return actorResponseHandler( + actorRef, request, timeout, null, httpRequest); + } catch (Exception e) { + return CompletableFuture.completedFuture(createCommonExceptionResponse(e, httpRequest)); + } + } + + public CompletionStage<Result> updateUserEventState(Http.Request httpRequest) { + try { + JsonNode requestData = httpRequest.body().asJson(); + Request request = (Request) mapper.RequestMapper.mapRequest(requestData, Request.class); + request = transformUserId(request); + Common.handleFixedBatchIdRequest(request); + request.getRequest().remove(JsonKey.FIXED_BATCH_ID); + request.getRequest().remove(JsonKey.ID); + validateAndSetRequest(request); + request.setOperation("updateConsumption"); + request.setRequestId(httpRequest.attrs().getOptional(Attrs.REQUEST_ID).orElse(null)); + request.setEnv(getEnvironment()); + return actorResponseHandler(actorRef, request, timeout, null, httpRequest); + } catch (Exception e) { + return CompletableFuture.completedFuture(createCommonExceptionResponse(e, httpRequest)); + } + } + + private void validateAndSetRequest(Request request) { + if (null == request.get(JsonKey.COURSE_ID) || request.get(JsonKey.COURSE_ID).toString().isEmpty()) { + throw new ProjectCommonException( + ResponseCode.courseIdRequired.getErrorCode(), + ResponseCode.courseIdRequired.getErrorMessage(), + ResponseCode.CLIENT_ERROR.getResponseCode()); + } + request.getRequest().put(JsonKey.CONTENT_ID, request.get(JsonKey.COURSE_ID)); + if (null == request.get(JsonKey.BATCH_ID) || request.get(JsonKey.BATCH_ID).toString().isEmpty()) { + throw new ProjectCommonException( + ResponseCode.courseBatchIdRequired.getErrorCode(), + ResponseCode.courseBatchIdRequired.getErrorMessage(), + ResponseCode.CLIENT_ERROR.getResponseCode()); + } + if (null == request.get(JsonKey.USER_ID) || request.get(JsonKey.USER_ID).toString().isEmpty()) { + throw new ProjectCommonException( + ResponseCode.userIdRequired.getErrorCode(), + ResponseCode.userIdRequired.getErrorMessage(), + ResponseCode.CLIENT_ERROR.getResponseCode()); + } + } + +} diff --git a/service/app/controllers/eventmanagement/EventController.java b/service/app/controllers/eventmanagement/EventController.java new file mode 100644 index 000000000..034a56a80 --- /dev/null +++ b/service/app/controllers/eventmanagement/EventController.java @@ -0,0 +1,39 @@ +package controllers.eventmanagement; + +import akka.actor.ActorRef; +import controllers.BaseController; +import controllers.eventmanagement.validator.EventRequestValidator; +import org.sunbird.common.models.util.ActorOperations; +import org.sunbird.common.models.util.LoggerEnum; +import org.sunbird.common.models.util.ProjectLogger; +import org.sunbird.common.request.Request; +import play.mvc.Http; +import play.mvc.Result; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.concurrent.CompletionStage; + +public class EventController extends BaseController { + + @Inject + @Named("event-management-actor") + private ActorRef actorRef; + + public CompletionStage<Result> discard(String id, Http.Request httpRequest) { + ProjectLogger.log( + "Discard event method is called = " + httpRequest.body().asJson(), + LoggerEnum.DEBUG.name()); + return handleRequest( + actorRef, + ActorOperations.DELETE_EVENT.getValue(), + httpRequest.body().asJson(), + (request) -> { + ((Request) request).getRequest().put("identifier", id); + EventRequestValidator.validateFixedBatchId((Request) request); + return null; + }, + httpRequest); + } + +} diff --git a/service/app/controllers/eventmanagement/EventSetController.java b/service/app/controllers/eventmanagement/EventSetController.java new file mode 100644 index 000000000..6f3b7e057 --- /dev/null +++ b/service/app/controllers/eventmanagement/EventSetController.java @@ -0,0 +1,53 @@ +package controllers.eventmanagement; + +import akka.actor.ActorRef; +import controllers.BaseController; +import controllers.eventmanagement.validator.EventRequestValidator; +import org.sunbird.common.models.util.ActorOperations; +import org.sunbird.common.models.util.LoggerEnum; +import org.sunbird.common.models.util.ProjectLogger; +import org.sunbird.common.request.Request; +import play.mvc.Http; +import play.mvc.Result; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.concurrent.CompletionStage; + +public class EventSetController extends BaseController { + + @Inject + @Named("eventset-management-actor") + private ActorRef actorRef; + + public CompletionStage<Result> update(Http.Request httpRequest) { + ProjectLogger.log( + LoggerEnum.DEBUG.name()); + return handleRequest( + actorRef, + ActorOperations.UPDATE_EVENT_SET.getValue(), + httpRequest.body().asJson(), + (request) -> { + EventRequestValidator.validateRequest((Request) request); + return null; + }, + httpRequest); + } + + public CompletionStage<Result> discard(String id, Http.Request httpRequest) { + ProjectLogger.log( + "Discard eventset method is called = " + httpRequest.body().asJson(), + LoggerEnum.DEBUG.name()); + return handleRequest( + actorRef, + ActorOperations.DELETE_EVENT_SET.getValue(), + httpRequest.body().asJson(), + (request) -> { + ((Request) request).getRequest().put("identifier", id); + EventRequestValidator.validateFixedBatchId((Request) request); + return null; + }, + httpRequest); + } + +} diff --git a/service/app/controllers/eventmanagement/validator/EventRequestValidator.java b/service/app/controllers/eventmanagement/validator/EventRequestValidator.java new file mode 100644 index 000000000..574704eaf --- /dev/null +++ b/service/app/controllers/eventmanagement/validator/EventRequestValidator.java @@ -0,0 +1,53 @@ +package controllers.eventmanagement.validator; + +import org.sunbird.common.exception.ProjectCommonException; +import org.sunbird.common.request.Request; +import org.sunbird.common.responsecode.ResponseCode; +import org.sunbird.keys.SunbirdKey; + +import java.text.MessageFormat; + +import static org.sunbird.common.models.util.JsonKey.FIXED_BATCH_ID; + +public class EventRequestValidator { + public static void validateRequest(Request request) { + try { + String message = ""; + if (null == request.get(SunbirdKey.IDENTIFIER)) { + message += "Error due to missing request identifier"; + ProjectCommonException.throwClientErrorException( + ResponseCode.missingData, + MessageFormat.format( + ResponseCode.missingData.getErrorMessage(), message)); + } + if (null == request.get(FIXED_BATCH_ID)) { + message += "request should have fixedBatchId"; + ProjectCommonException.throwClientErrorException( + ResponseCode.missingData, + MessageFormat.format( + ResponseCode.missingData.getErrorMessage(), message)); + } + if (!request.getRequest().getOrDefault(SunbirdKey.OBJECT_TYPE, "").equals("EventSet")) { + message += "context objectType should be EventSet"; + ProjectCommonException.throwClientErrorException( + ResponseCode.contentTypeMismatch, + MessageFormat.format( + ResponseCode.contentTypeMismatch.getErrorMessage(), message)); + } + } catch (Exception ex) { + throw ex; + } + } + + public static void validateFixedBatchId(Request request) { + if (null == request.get(FIXED_BATCH_ID)) { + String message = "Error due to missing fixedBatchId"; + ProjectCommonException.throwClientErrorException( + ResponseCode.missingData, + MessageFormat.format( + ResponseCode.missingData.getErrorMessage(), message)); + } + } + + +} diff --git a/service/app/util/ACTOR_NAMES.java b/service/app/util/ACTOR_NAMES.java index 41aa6b42f..53e8bd322 100644 --- a/service/app/util/ACTOR_NAMES.java +++ b/service/app/util/ACTOR_NAMES.java @@ -1,9 +1,7 @@ package util; import org.sunbird.aggregate.CollectionSummaryAggregate; -import org.sunbird.enrolments.CourseEnrolmentActor; -import org.sunbird.enrolments.ContentConsumptionActor; -import org.sunbird.enrolments.CourseEnrolmentActor; +import org.sunbird.enrolments.*; import org.sunbird.group.GroupAggregatesActor; import org.sunbird.learner.actors.BackgroundJobManager; import org.sunbird.learner.actors.PageManagementActor; @@ -15,6 +13,8 @@ import org.sunbird.learner.actors.course.CourseManagementActor; import org.sunbird.learner.actors.coursebatch.CourseBatchManagementActor; import org.sunbird.learner.actors.coursebatch.CourseBatchNotificationActor; +import org.sunbird.learner.actors.event.EventManagementActor; +import org.sunbird.learner.actors.event.EventSetManagementActor; import org.sunbird.learner.actors.health.HealthActor; import org.sunbird.learner.actors.qrcodedownload.QRCodeDownloadManagementActor; import org.sunbird.learner.actors.search.SearchHandlerActor; @@ -41,6 +41,10 @@ public enum ACTOR_NAMES { COURSE_BATCH_NOTIFICATION_ACTOR(CourseBatchNotificationActor.class, "course-batch-notification-actor"), BACKGROUND_JOB_MANAGER_ACTOR(BackgroundJobManager.class, "background-job-manager-actor"), COURSE_MANAGEMENT_ACTOR(CourseManagementActor.class, "course-management-actor"), + EVENT_SET_MANAGEMENT_ACTOR(EventSetManagementActor.class, "eventset-management-actor"), + EVENT_MANAGEMENT_ACTOR(EventManagementActor.class, "event-management-actor"), + EVENT_SET_ENROLMENT_ACTOR(EventSetEnrolmentActor.class, "eventset-enrolment-actor"), + EVENT_CONSUMPTION_ACTOR(EventConsumptionActor.class, "event-consumption-actor"), //Scala Actors COURSE_ENROLMENT_ACTOR(CourseEnrolmentActor.class, "course-enrolment-actor"), CONTENT_CONSUMPTION_ACTOR(ContentConsumptionActor.class, "content-consumption-actor"), diff --git a/service/conf/application.conf b/service/conf/application.conf index 0307054be..2300a3041 100644 --- a/service/conf/application.conf +++ b/service/conf/application.conf @@ -205,6 +205,30 @@ akka { nr-of-instances = 4 dispatcher = rr-dispatcher } + /eventset-management-actor + { + router = smallest-mailbox-pool + nr-of-instances = 4 + dispatcher = rr-dispatcher + } + /event-management-actor + { + router = smallest-mailbox-pool + nr-of-instances = 4 + dispatcher = rr-dispatcher + } + /eventset-enrolment-actor + { + router = smallest-mailbox-pool + nr-of-instances = 4 + dispatcher = rr-dispatcher + } + /event-consumption-actor + { + router = smallest-mailbox-pool + nr-of-instances = 4 + dispatcher = rr-dispatcher + } } } } diff --git a/service/conf/routes b/service/conf/routes index 23a148f02..c048f589d 100644 --- a/service/conf/routes +++ b/service/conf/routes @@ -19,6 +19,8 @@ POST /v2/user/courses/list @controllers.courseenrollment.CourseEnrollmen POST /private/v2/user/courses/list @controllers.courseenrollment.CourseEnrollmentController.privateGetUserEnrolledCourses(request: play.mvc.Http.Request) POST /v1/course/enroll @controllers.courseenrollment.CourseEnrollmentController.enrollCourse(request: play.mvc.Http.Request) POST /v1/course/unenroll @controllers.courseenrollment.CourseEnrollmentController.unenrollCourse(request: play.mvc.Http.Request) +POST /v1/course/admin/enroll @controllers.courseenrollment.CourseEnrollmentController.adminEnrollCourse(request: play.mvc.Http.Request) +POST /v1/course/admin/unenroll @controllers.courseenrollment.CourseEnrollmentController.adminUnenrollCourse(request: play.mvc.Http.Request) POST /v1/batch/bulk/enrollment @controllers.bulkapimanagement.BulkUploadController.batchEnrollmentBulkUpload(request: play.mvc.Http.Request) POST /v1/batch/bulk/unenrollment @controllers.bulkapimanagement.BulkUploadController.batchUnEnrollmentBulkUpload(request: play.mvc.Http.Request) POST /v1/content/state/read @controllers.LearnerController.getContentState(request: play.mvc.Http.Request) @@ -72,4 +74,18 @@ POST /v1/course/create @controllers.coursemanagement.CourseControll POST /v1/group/activity/agg @controllers.group.GroupAggController.getGroupActivityAggregates(request: play.mvc.Http.Request) #Summary Aggregate -POST /v1/collection/summary @controllers.collectionsummaryaggregate.CollectionSummaryAggregateController.getCollectionSummaryAggregate(request: play.mvc.Http.Request) \ No newline at end of file +POST /v1/collection/summary @controllers.collectionsummaryaggregate.CollectionSummaryAggregateController.getCollectionSummaryAggregate(request: play.mvc.Http.Request) + +# Event Management APIs +GET /v1/user/event/list/:uid @controllers.courseenrollment.CourseEnrollmentController.getEnrolledCourses(uid: String, request: play.mvc.Http.Request) +POST /v1/event/enroll @controllers.courseenrollment.CourseEnrollmentController.enrollCourse(request: play.mvc.Http.Request) +POST /v1/event/unenroll @controllers.courseenrollment.CourseEnrollmentController.unenrollCourse(request: play.mvc.Http.Request) +GET /v1/event/participants/list @controllers.courseenrollment.CourseEnrollmentController.getParticipantsForFixedBatch(request: play.mvc.Http.Request) +POST /v1/eventset/enroll @controllers.eventenrollment.EventSetEnrollmentController.enroll(request: play.mvc.Http.Request) +POST /v1/eventset/unenroll @controllers.eventenrollment.EventSetEnrollmentController.unenroll(request: play.mvc.Http.Request) +PATCH /v1/eventset/update @controllers.eventmanagement.EventSetController.update(request: play.mvc.Http.Request) +DELETE /v1/eventset/discard/:id @controllers.eventmanagement.EventSetController.discard(id: String, request: play.mvc.Http.Request) +DELETE /v1/event/discard/:id @controllers.eventmanagement.EventController.discard(id: String, request: play.mvc.Http.Request) +POST /v1/user/event/state/read @controllers.eventmanagement.EventConsumptionController.getUserEventState(request: play.mvc.Http.Request) +PATCH /v1/user/event/state/update @controllers.eventmanagement.EventConsumptionController.updateUserEventState(request: play.mvc.Http.Request) +