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)
+