Skip to content

Commit

Permalink
4.8.16 dev (#160)
Browse files Browse the repository at this point in the history
* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment

1. Added a custom logic processStartEndDate that is exceuted if the starttime and entime provided from the ui request body is not null.

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment

1. Fixed the time issue.

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment

1. Added the time stamp to elastic search & the collection object while saving.

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment

1.Add 5hrs30mins to the current time stamp, made it configurable based on flag.
2.Add values to string constants.

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment. (#155)

1. Created  a new variable primaryCategory to check if it is a standalone assessment.
2. Added a new validation to check if its a standalone assessment primary category.
3. So the new if case checks the entire timestamp from cassandra db .

* Revert " KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of…" (#157)

This reverts commit 5171d6f.

* private api added for the enrolment list. (#159)

* private api added for the mentoring competecies for the user

* changes according to review comments

* changes according to review comments

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment. (#158)

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment.

1.Created a new variable primaryCategory to check if it is a standalone assessment.
2. Added a new validation to check if its a standalone assessment primary category.
3. So the new if case checks the entire timestamp from cassandra db .

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment.

1. Add a custom logic to check the enrollment date is future wrt current time stamp.

* KB-5718 | DEV|Assessment |BE| Capturing the starttime and endtime of the Batches while creating the batch for Assessment.

1. Added the logic for admin enrolling of user for program api also.

---------

Co-authored-by: tarentomaheshvakkund <139739142+tarentomaheshvakkund@users.noreply.github.com>
Co-authored-by: shankaragoudab <140387294+shankaragoudab@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 10, 2024
1 parent cf8b6a9 commit 08e9490
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
import org.sunbird.learner.util.Util;
import org.sunbird.models.course.batch.CourseBatch;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import org.sunbird.common.models.util.ProjectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CourseBatchDaoImpl implements CourseBatchDao {
private CassandraOperation cassandraOperation = ServiceFactory.getInstance();
Expand All @@ -27,13 +30,27 @@ public class CourseBatchDaoImpl implements CourseBatchDao {
CassandraPropertyReader.getInstance();
private ObjectMapper mapper = new ObjectMapper();
private String dateFormat = "yyyy-MM-dd";

private static final Logger log = LoggerFactory.getLogger(CourseBatchDaoImpl.class);

@Override
public Response create(RequestContext requestContext, CourseBatch courseBatch) {
Map<String, Object> map = CourseBatchUtil.cassandraCourseMapping(courseBatch, dateFormat);
map = CassandraUtil.changeCassandraColumnMapping(map);
CassandraUtil.convertMaptoJsonString(map, JsonKey.BATCH_ATTRIBUTES_KEY);
if(map.get(JsonKey.START_TIME) != null) {
String dateType=JsonKey.START_DATE_BATCH ;
String timeType=JsonKey.START_TIME;
processStartEndDate(map, timeType, dateType);
map.remove(JsonKey.START_TIME);
courseBatch.setStartDate((Date)map.get(dateType));
}
if(map.get(JsonKey.END_TIME) != null) {
String dateType=JsonKey.END_DATE_BATCH;
String timeType=JsonKey.END_TIME;
processStartEndDate(map, timeType, dateType);
map.remove(JsonKey.END_TIME);
courseBatch.setEndDate((Date)map.get(dateType));
}
return cassandraOperation.insertRecord(
requestContext, courseBatchDb.getKeySpace(), courseBatchDb.getTableName(), map);
}
Expand Down Expand Up @@ -150,4 +167,43 @@ public CourseBatch readFirstAvailableBatch(String courseId, RequestContext reque
ResponseCode.CLIENT_ERROR.getResponseCode());
}
}


/**
* This method processes the start and end date by merging the time part from the given map
* into the date part from the given map and setting it to the specified timezone.
*
* @param map The map containing the time and date information.
* @param timeType The key in the map for the time string.
* @param dateType The key in the map for the date object.
*/
private static void processStartEndDate(Map<String, Object> map, String timeType, String dateType) {
String timeStr = (String) map.get(timeType);
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
Date time;
try {
time = timeFormat.parse(timeStr);
} catch (ParseException e) {
log.error("Failed to parse time string: " + timeStr, e);
return;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime((Date) map.get(dateType));
Calendar timeCalendar = Calendar.getInstance();
timeCalendar.setTime(time);
String timeZone = ProjectUtil.getConfigValue(JsonKey.SUNBIRD_TIMEZONE);
TimeZone tz = TimeZone.getTimeZone(timeZone);
calendar.setTimeZone(tz);
log.info("Merging time part {} into date {} with timezone {}", timeStr, map.get(dateType), timeZone);
calendar.set(Calendar.HOUR_OF_DAY, timeCalendar.get(Calendar.HOUR_OF_DAY));
calendar.set(Calendar.MINUTE, timeCalendar.get(Calendar.MINUTE));
calendar.set(Calendar.SECOND, timeCalendar.get(Calendar.SECOND));
if(ProjectUtil.getConfigValue(JsonKey.ADD_EXTRA_HOURS_MINS).equalsIgnoreCase("true")){
calendar.add(Calendar.HOUR_OF_DAY, 5);
calendar.add(Calendar.MINUTE, 30);
log.info("Added 5hours 30mins to the start_date and end_date");
}
map.put(dateType, calendar.getTime());
log.info("Updated date in map with key {}: {}", dateType, calendar.getTime());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ public static Map<String, Object> esCourseMapping(CourseBatch courseBatch, Strin
dateFormat.setTimeZone(TimeZone.getTimeZone(ProjectUtil.getConfigValue(JsonKey.SUNBIRD_TIMEZONE)));
dateTimeFormat.setTimeZone(TimeZone.getTimeZone(ProjectUtil.getConfigValue(JsonKey.SUNBIRD_TIMEZONE)));
Map<String, Object> esCourseMap = mapper.convertValue(courseBatch, Map.class);
if (courseBatch.getStartTime() != null && courseBatch.getEndTime() != null) {
esCourseMap.put(JsonKey.START_TIME, courseBatch.getStartDate());
esCourseMap.put(JsonKey.END_TIME, courseBatch.getEndDate());
}
changeInDateFormat.forEach(key -> {
if (null != esCourseMap.get(key))
esCourseMap.put(key, dateTimeFormat.format(esCourseMap.get(key)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,27 @@ public class CourseBatch implements Serializable {
private String hashTagId;
private List<String> mentors;
private String name;
private String startTime;
private String endTime;

private Integer status;

public String getStartTime() {
return startTime;
}

public void setStartTime(String startTime) {
this.startTime = startTime;
}

public String getEndTime() {
return endTime;
}

public void setEndTime(String endTime) {
this.endTime = endTime;
}

private Map<String, Object> certTemplates;
private Map<String, Object> batchAttributes;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,8 @@ private void updateCollection(RequestContext requestContext, Map<String, Object>
data.put("createdFor", courseBatch.getOrDefault(JsonKey.COURSE_CREATED_FOR, new ArrayList<>()));
data.put("startDate", courseBatch.getOrDefault(JsonKey.START_DATE, ""));
data.put("endDate", courseBatch.getOrDefault(JsonKey.END_DATE, null));
data.put("startTime", courseBatch.getOrDefault(JsonKey.START_TIME, null));
data.put("endTime", courseBatch.getOrDefault(JsonKey.END_TIME, null));
data.put("enrollmentType", courseBatch.getOrDefault(JsonKey.ENROLLMENT_TYPE, ""));
data.put("status", courseBatch.getOrDefault(JsonKey.STATUS, ""));
data.put("batchAttributes", courseBatch.getOrDefault(CourseJsonKey.BATCH_ATTRIBUTES, new HashMap<String, Object>()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.text.{MessageFormat, SimpleDateFormat}
import java.time.format.DateTimeFormatter
import java.time.{LocalDate, LocalDateTime, LocalTime, Month, ZoneId}
import java.util
import java.util.{Collections, Comparator, Date, UUID}
import java.util.{Calendar, Collections, Comparator, Date, TimeZone, UUID}
import akka.actor.ActorRef
import com.fasterxml.jackson.databind.ObjectMapper
import org.sunbird.common.models.util.JsonKey
Expand Down Expand Up @@ -579,6 +579,12 @@ class CourseEnrolmentActor @Inject()(@Named("course-batch-notification-actor") c
}
}
val batchUserData: BatchUser = batchUserDao.read(request.getRequestContext, batchId, userId)
val primaryCategory=contentData.get(JsonKey.PRIMARYCATEGORY).asInstanceOf[String]
if(primaryCategory.equalsIgnoreCase(JsonKey.STANDALONE_ASSESSMENT)) {
validateEnrolmentV2(batchData, enrolmentData, true,primaryCategory)
}else{
validateEnrolment(batchData, enrolmentData, true)
}
validateEnrolment(batchData, enrolmentData, true)
getCoursesForProgramAndEnrol(request, programId, userId, batchId)
val dataBatch: util.Map[String, AnyRef] = createBatchUserMapping(batchId, userId, batchUserData)
Expand Down Expand Up @@ -785,7 +791,12 @@ class CourseEnrolmentActor @Inject()(@Named("course-batch-notification-actor") c
}
}
val batchUserData: BatchUser = batchUserDao.read(request.getRequestContext, batchId, userId)
validateEnrolment(batchData, enrolmentData, true)
val primaryCategory=contentData.get(JsonKey.PRIMARYCATEGORY).asInstanceOf[String]
if(primaryCategory.equalsIgnoreCase(JsonKey.STANDALONE_ASSESSMENT)) {
validateEnrolmentV2(batchData, enrolmentData, true,primaryCategory)
}else{
validateEnrolment(batchData, enrolmentData, true)
}
getCoursesForProgramAndEnrol(request, programId, userId, batchId)
val dataBatch: util.Map[String, AnyRef] = createBatchUserMapping(batchId, userId, batchUserData)
val data: java.util.Map[String, AnyRef] = createUserEnrolmentMap(userId, programId, batchId, enrolmentData, request.getContext.getOrDefault(JsonKey.REQUEST_ID, "").asInstanceOf[String])
Expand Down Expand Up @@ -817,6 +828,47 @@ class CourseEnrolmentActor @Inject()(@Named("course-batch-notification-actor") c
}
sender().tell(resp, self)
}

/**
* Validates enrolment based on various conditions.
*
* @param batchData CourseBatch object containing batch details.
* @param enrolmentData UserCourses object containing user's enrolment details.
* @param isEnrol Boolean indicating whether user is attempting to enrol or not.
* @param primaryCategory Primary category of the course.
*/
def validateEnrolmentV2(batchData: CourseBatch, enrolmentData: UserCourses, isEnrol: Boolean,primaryCategory: String): Unit = {
if(null == batchData)
ProjectCommonException.throwClientErrorException(ResponseCode.invalidCourseBatchId, ResponseCode.invalidCourseBatchId.getErrorMessage)

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))))
ProjectCommonException.throwClientErrorException(ResponseCode.courseBatchAlreadyCompleted, ResponseCode.courseBatchAlreadyCompleted.getErrorMessage)

if(primaryCategory.equalsIgnoreCase(JsonKey.STANDALONE_ASSESSMENT) && isEnrol && null != batchData.getEnrollmentEndDate &&
isFutureDate(batchData.getEnrollmentEndDate))
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)
}

/**
* Checks if the given enrollment date is a future date compared to the current date/time.
*
* @param enrollmentEndDate The date to be checked for being in the future.
* @return `true` if the enrollment end date is in the future; `false` otherwise.
*/
def isFutureDate(enrollmentEndDate: Date): Boolean = {
val inputCal = Calendar.getInstance(TimeZone.getTimeZone(ProjectUtil.getConfigValue(JsonKey.SUNBIRD_TIMEZONE)));
inputCal.setTime(enrollmentEndDate)
val currentCal = Calendar.getInstance(TimeZone.getTimeZone(ProjectUtil.getConfigValue(JsonKey.SUNBIRD_TIMEZONE)));
currentCal.after(inputCal)
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1139,5 +1139,9 @@ public final class JsonKey {
public static final String LRC_PROGRESS_DETAILS = "lrcProgressDetails";
public static final String USERS_COUNT = "system.count(userid)";
public static final String COURSE_ENROLL_ALLOWED_PRIMARY_CATEGORY = "course_enroll_allowed_primary_category";
public static final String START_DATE_BATCH = "start_date";
public static final String END_DATE_BATCH = "end_date";
public static final String ADD_EXTRA_HOURS_MINS = "addExtraHrsAndMins.start_date_end_date";
public static final String STANDALONE_ASSESSMENT ="Standalone Assessment";
private JsonKey() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,4 @@ content_bucket=/content-store/content
static_host_url=https://static.karmayogiprod.nic.in
profile_update_url=/app/user-profile/details
course_enroll_allowed_primary_category=Course,Blended Program,Standalone Assessment
addExtraHrsAndMins.start_date_end_date=true
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class CourseEnrollmentController extends BaseController {

private CourseEnrollmentRequestValidator validator = new CourseEnrollmentRequestValidator();

public CompletionStage<Result> getEnrolledCourses(String uid, Http.Request httpRequest,String version) {
public CompletionStage<Result> getEnrolledCourses(String uid, Http.Request httpRequest,String version,boolean isPrivate) {
return handleRequest(courseEnrolmentActor, "listEnrol",
httpRequest.body().asJson(),
(req) -> {
Expand All @@ -42,6 +42,9 @@ public CompletionStage<Result> getEnrolledCourses(String uid, Http.Request httpR
queryParams.put("fields", fields.toArray(new String[0]));
}
String userId = (String) request.getContext().getOrDefault(JsonKey.REQUESTED_FOR, request.getContext().get(JsonKey.REQUESTED_BY));
if(isPrivate){
userId = uid;
}
validator.validateRequestedBy(userId);
request.getContext().put(JsonKey.USER_ID, userId);
request.getRequest().put(JsonKey.USER_ID, userId);
Expand Down Expand Up @@ -264,10 +267,14 @@ public CompletionStage<Result> adminGetUserEnrolledCourses(Http.Request httpRequ
}

public CompletionStage<Result> getEnrolledCourses_v1(String uid, Http.Request httpRequest) {
return getEnrolledCourses(uid,httpRequest,"v1");
return getEnrolledCourses(uid,httpRequest,"v1", false);
}
public CompletionStage<Result> getEnrolledCourses_v2(String uid, Http.Request httpRequest) {
return getEnrolledCourses(uid, httpRequest, "v2");
return getEnrolledCourses(uid, httpRequest, "v2", false );
}

public CompletionStage<Result> privateGetUserEnrolledCourses_v3(String uid, Http.Request httpRequest) {
return getEnrolledCourses(uid, httpRequest, "v2", true);
}

public CompletionStage<Result> enrollProgram(Http.Request httpRequest, Boolean batchType) {
Expand Down
1 change: 1 addition & 0 deletions service/app/util/RequestInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ private RequestInterceptor() {}
apiHeaderIgnoreMap.put("/v1/course/admin/enroll", var);
apiHeaderIgnoreMap.put("/v1/course/admin/unenroll", var);
apiHeaderIgnoreMap.put("/v1/activate-started/course/batches/status", var);
apiHeaderIgnoreMap.put("/private/v3/user/courses/list/:uid", var);
}

/**
Expand Down
1 change: 1 addition & 0 deletions service/conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ DELETE /v1/cache/clear/:mapName @controllers.cache.CacheController.clearCache(ma
# Course Management APIs
GET /v1/user/courses/list/:uid @controllers.courseenrollment.CourseEnrollmentController.getEnrolledCourses_v1(uid:String, request: play.mvc.Http.Request)
GET /v2/user/courses/list/:uid @controllers.courseenrollment.CourseEnrollmentController.getEnrolledCourses_v2(uid:String, request: play.mvc.Http.Request)
GET /private/v3/user/courses/list/:uid @controllers.courseenrollment.CourseEnrollmentController.privateGetUserEnrolledCourses_v3(uid:String, request: play.mvc.Http.Request)
GET /private/v1/user/courses/list/:uid @controllers.courseenrollment.CourseEnrollmentController.privateGetEnrolledCourses(uid:String, request: play.mvc.Http.Request)
POST /v2/user/courses/list @controllers.courseenrollment.CourseEnrollmentController.getUserEnrolledCourses(request: play.mvc.Http.Request)
POST /v2/user/courses/admin/list @controllers.courseenrollment.CourseEnrollmentController.adminGetUserEnrolledCourses(request: play.mvc.Http.Request)
Expand Down

0 comments on commit 08e9490

Please sign in to comment.