Skip to content

Commit

Permalink
Merge pull request #808 from project-sunbird/release-4.7.0
Browse files Browse the repository at this point in the history
Issue #SB-29374 merge: From release-4.7.0 to master
  • Loading branch information
maheshkumargangula authored Mar 29, 2022
2 parents ddee3ca + 1241b1a commit 39eac28
Show file tree
Hide file tree
Showing 38 changed files with 1,411 additions and 466 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ class QuestionSetActor @Inject()(implicit oec: OntologyEngineContext) extends Ba
request.getRequest.put("mode", "edit")
AssessmentManager.getValidatedNodeForReview(request, "ERR_QUESTION_SET_REVIEW").flatMap(node => {
AssessmentManager.getQuestionSetHierarchy(request, node).flatMap(hierarchyString => {
AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String])
val (updatedHierarchy, nodeIds) = AssessmentManager.updateHierarchy(hierarchyString.asInstanceOf[String], "Review")
AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String], node.getMetadata.getOrDefault("createdBy", "").asInstanceOf[String])
val (updatedHierarchy, nodeIds) = AssessmentManager.updateHierarchy(hierarchyString.asInstanceOf[String], "Review", node.getMetadata.getOrDefault("createdBy", "").asInstanceOf[String])
val updateReq = new Request(request)
val date = DateUtils.formatCurrentDate
updateReq.putAll(Map("identifiers" -> nodeIds, "metadata" -> Map("status" -> "Review", "prevStatus" -> node.getMetadata.get("status"), "lastStatusChangedOn" -> date, "lastUpdatedOn" -> date).asJava).asJava)
Expand All @@ -66,9 +66,10 @@ class QuestionSetActor @Inject()(implicit oec: OntologyEngineContext) extends Ba

def publish(request: Request): Future[Response] = {
request.getRequest.put("identifier", request.getContext.get("identifier"))
request.put("mode", "edit")
AssessmentManager.getValidatedNodeForPublish(request, "ERR_QUESTION_SET_PUBLISH").flatMap(node => {
AssessmentManager.getQuestionSetHierarchy(request, node).map(hierarchyString => {
AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String])
AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String], node.getMetadata.getOrDefault("createdBy", "").asInstanceOf[String])
AssessmentManager.pushInstructionEvent(node.getIdentifier, node)
ResponseHandler.OK.putAll(Map[String, AnyRef]("identifier" -> node.getIdentifier.replace(".img", ""), "message" -> "Question is successfully sent for Publish").asJava)
})
Expand All @@ -93,8 +94,8 @@ class QuestionSetActor @Inject()(implicit oec: OntologyEngineContext) extends Ba
request.getRequest.put("mode", "edit")
AssessmentManager.getValidateNodeForReject(request, "ERR_QUESTION_SET_REJECT").flatMap(node => {
AssessmentManager.getQuestionSetHierarchy(request, node).flatMap(hierarchyString => {
AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String])
val (updatedHierarchy, nodeIds) = AssessmentManager.updateHierarchy(hierarchyString.asInstanceOf[String], "Draft")
AssessmentManager.validateQuestionSetHierarchy(hierarchyString.asInstanceOf[String], node.getMetadata.getOrDefault("createdBy", "").asInstanceOf[String])
val (updatedHierarchy, nodeIds) = AssessmentManager.updateHierarchy(hierarchyString.asInstanceOf[String], "Draft", node.getMetadata.getOrDefault("createdBy", "").asInstanceOf[String])
val updateReq = new Request(request)
val date = DateUtils.formatCurrentDate
updateReq.putAll(Map("identifiers" -> nodeIds, "metadata" -> Map("status" -> "Draft", "prevStatus" -> node.getMetadata.get("status"), "lastStatusChangedOn" -> date, "lastUpdatedOn" -> date).asJava).asJava)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import scala.collection.JavaConverters._

object AssessmentManager {

val skipValidation: Boolean = Platform.getBoolean("assessment.skip.validation", true)
val skipValidation: Boolean = Platform.getBoolean("assessment.skip.validation", false)
val validStatus = List("Draft", "Review")

def create(request: Request, errCode: String)(implicit oec: OntologyEngineContext, ec: ExecutionContext): Future[Response] = {
val visibility: String = request.getRequest.getOrDefault("visibility", "").asInstanceOf[String]
Expand Down Expand Up @@ -128,36 +129,34 @@ object AssessmentManager {
})
}

def validateQuestionSetHierarchy(hierarchyString: String)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Unit = {
def validateQuestionSetHierarchy(hierarchyString: String, rootUserId: String)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Unit = {
if (!skipValidation) {
val hierarchy = if (!hierarchyString.asInstanceOf[String].isEmpty) {
JsonUtils.deserialize(hierarchyString.asInstanceOf[String], classOf[java.util.Map[String, AnyRef]])
} else
new java.util.HashMap[String, AnyRef]()
val children = hierarchy.getOrDefault("children", new util.ArrayList[java.util.Map[String, AnyRef]]).asInstanceOf[util.List[java.util.Map[String, AnyRef]]]
validateChildrenRecursive(children)
validateChildrenRecursive(children, rootUserId)
}
}

def getQuestionSetHierarchy(request: Request, rootNode: Node)(implicit ec: ExecutionContext, oec: OntologyEngineContext): Future[Any] = {
oec.graphService.readExternalProps(request, List("hierarchy")).flatMap(response => {
if (ResponseHandler.checkError(response) && ResponseHandler.isResponseNotFoundError(response)) {
if (StringUtils.equalsIgnoreCase("Live", rootNode.getMetadata.get("status").asInstanceOf[String]))
throw new ServerException("ERR_QUESTION_SET_REVIEW", "No hierarchy is present in cassandra for identifier:" + rootNode.getIdentifier)
request.put("identifier", if (!rootNode.getIdentifier.endsWith(".img")) rootNode.getIdentifier + ".img" else rootNode.getIdentifier)
oec.graphService.readExternalProps(request, List("hierarchy")).map(resp => {
resp.getResult.toMap.getOrElse("hierarchy", "{}").asInstanceOf[String]
}) recover { case e: ResourceNotFoundException => TelemetryManager.log("No hierarchy is present in cassandra for identifier:" + request.get("identifier")) }
} else Future(response.getResult.toMap.getOrElse("hierarchy", "{}").asInstanceOf[String])
request.put("rootId", request.get("identifier").asInstanceOf[String])
HierarchyManager.getUnPublishedHierarchy(request).map(resp => {
if (!ResponseHandler.checkError(resp) && resp.getResponseCode.code() == 200) {
val hierarchy = resp.getResult.get("questionSet").asInstanceOf[util.Map[String, AnyRef]]
JsonUtils.serialize(hierarchy)
} else throw new ServerException("ERR_QUESTION_SET_HIERARCHY", "No hierarchy is present in cassandra for identifier:" + rootNode.getIdentifier)
})
}

private def validateChildrenRecursive(children: util.List[util.Map[String, AnyRef]]): Unit = {
private def validateChildrenRecursive(children: util.List[util.Map[String, AnyRef]], rootUserId: String): Unit = {
children.toList.foreach(content => {
if (!StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Parent")
if ((StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Default")
&& !StringUtils.equals(rootUserId, content.getOrDefault("createdBy", "").asInstanceOf[String]))
&& !StringUtils.equalsIgnoreCase(content.getOrDefault("status", "").asInstanceOf[String], "Live"))
throw new ClientException("ERR_QUESTION_SET", "Content with identifier: " + content.get("identifier") + "is not Live. Please Publish it.")
validateChildrenRecursive(content.getOrDefault("children", new util.ArrayList[Map[String, AnyRef]]).asInstanceOf[util.List[util.Map[String, AnyRef]]])
throw new ClientException("ERR_QUESTION_SET", "Object with identifier: " + content.get("identifier") + " is not Live. Please Publish it.")
validateChildrenRecursive(content.getOrDefault("children", new util.ArrayList[Map[String, AnyRef]]).asInstanceOf[util.List[util.Map[String, AnyRef]]], rootUserId)
})
}

Expand All @@ -169,28 +168,32 @@ object AssessmentManager {
(visibilityIdMap.getOrDefault("Default", List()), visibilityIdMap.getOrDefault("Parent", List()))
}

def updateHierarchy(hierarchyString: String, status: String): (java.util.Map[String, AnyRef], java.util.List[String]) = {
val hierarchy = if (!hierarchyString.asInstanceOf[String].isEmpty) {
def updateHierarchy(hierarchyString: String, status: String, rootUserId: String): (java.util.Map[String, AnyRef], java.util.List[String]) = {
val hierarchy: java.util.Map[String, AnyRef] = if (!hierarchyString.asInstanceOf[String].isEmpty) {
JsonUtils.deserialize(hierarchyString.asInstanceOf[String], classOf[java.util.Map[String, AnyRef]])
} else
new java.util.HashMap[String, AnyRef]()
val keys = List("identifier", "children").asJava
hierarchy.keySet().retainAll(keys)
val children = hierarchy.getOrDefault("children", new util.ArrayList[java.util.Map[String, AnyRef]]).asInstanceOf[util.List[java.util.Map[String, AnyRef]]]
hierarchy.put("status", status)
val childrenToUpdate: List[String] = updateChildrenRecursive(children, status, List())
val childrenToUpdate: List[String] = updateChildrenRecursive(children, status, List(), rootUserId)
(hierarchy, childrenToUpdate.asJava)
}

private def updateChildrenRecursive(children: util.List[util.Map[String, AnyRef]], status: String, idList: List[String]): List[String] = {
private def updateChildrenRecursive(children: util.List[util.Map[String, AnyRef]], status: String, idList: List[String], rootUserId: String): List[String] = {
children.toList.flatMap(content => {
val objectType = content.getOrDefault("objectType", "").asInstanceOf[String]
val updatedIdList: List[String] =
if (StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Parent")) {
if (StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Parent") || (StringUtils.equalsIgnoreCase( objectType, "Question") && StringUtils.equalsAnyIgnoreCase(content.getOrDefault("visibility", "").asInstanceOf[String], "Default") && validStatus.contains(content.getOrDefault("status", "").asInstanceOf[String]) && StringUtils.equals(rootUserId, content.getOrDefault("createdBy", "").asInstanceOf[String]))) {
content.put("lastStatusChangedOn", DateUtils.formatCurrentDate)
content.put("prevStatus", content.getOrDefault("status", "Draft"))
content.put("status", status)
content.put("prevStatus", "Draft")
content.put("lastUpdatedOn", DateUtils.formatCurrentDate)
content.get("identifier").asInstanceOf[String] :: idList
if(StringUtils.equalsAnyIgnoreCase(objectType, "Question")) content.get("identifier").asInstanceOf[String] :: idList else idList
} else idList
val list = updateChildrenRecursive(content.getOrDefault("children", new util.ArrayList[Map[String, AnyRef]]).asInstanceOf[util.List[util.Map[String, AnyRef]]], status, updatedIdList)
val list = updateChildrenRecursive(content.getOrDefault("children", new util.ArrayList[Map[String, AnyRef]]).asInstanceOf[util.List[util.Map[String, AnyRef]]], status, updatedIdList, rootUserId)
list ++ updatedIdList
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,20 @@ class BaseSpec extends FlatSpec with Matchers {
})
util.Arrays.asList(node)
}

def getNode(identifier: String, objectType: String, metadata: Option[util.Map[String, AnyRef]]): Node = {
val node = new Node("domain", "DATA_NODE", objectType)
node.setGraphId("domain")
val nodeMetadata = metadata.getOrElse(new util.HashMap[String, AnyRef]() {{
put("name", "Question 1")
put("code", "ques.1")
put("status", "Draft")
put("primaryCategory", "Multiple Choice Question")
}})
node.setNodeType("DATA_NODE")
node.setMetadata(nodeMetadata)
node.setObjectType(objectType)
node.setIdentifier(identifier)
node
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ class QuestionSetActorTest extends BaseSpec with MockFactory {
val kfClient = mock[KafkaClient]
(oec.kafkaClient _).expects().returns(kfClient).anyNumberOfTimes()
(oec.graphService _).expects().returns(graphDB).anyNumberOfTimes()
val node = getNode("QuestionSet", None)
val node = getNode("do_11348469558523494411","QuestionSet", None)
node.getMetadata.putAll(mapAsJavaMap(Map("name" -> "question_1",
"visibility" -> "Default",
"code" -> "finemanfine",
Expand All @@ -268,9 +268,15 @@ class QuestionSetActorTest extends BaseSpec with MockFactory {
"showHints" -> "Yes",
"summaryType" -> "Complete",
"mimeType" -> "application/vnd.sunbird.questionset",
"createdBy" -> "g-001",
"primaryCategory" -> "Practice Question Set")))
val nodeList = new util.ArrayList[Node]() {{
add(getNode("do_11348469662446387212","Question", Some(Map("visibility"-> "Parent", "createdBy"-> "g-001"))))
add(getNode("do_11348469662607769614","Question", Some(Map("visibility"-> "Default", "createdBy"-> "g-002"))))
}}
(graphDB.getNodeByUniqueId(_: String, _: String, _: Boolean, _: Request)).expects(*, *, *, *).returns(Future(node)).atLeastOnce()
(graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getCassandraHierarchy())).anyNumberOfTimes
(graphDB.getNodeByUniqueIds(_ :String, _: SearchCriteria)).expects(*, *).returns(Future(nodeList)).atLeastOnce()
(graphDB.readExternalProps(_: Request, _: List[String])).expects(*, *).returns(Future(getDraftCassandraHierarchy)).anyNumberOfTimes
(kfClient.send(_: String, _: String)).expects(*, *).once()
val request = getQuestionSetRequest()
request.getContext.put("identifier", "do1234")
Expand Down Expand Up @@ -600,6 +606,12 @@ class QuestionSetActorTest extends BaseSpec with MockFactory {
response.put("hierarchy", hierarchyString)
}

def getDraftCassandraHierarchy(): Response = {
val hierarchyString: String = """{"identifier":"do_11348469558523494411","children":[{"parent":"do_11348469558523494411","code":"Q1","description":"Q1","language":["English"],"mimeType":"application/vnd.sunbird.question","createdOn":"2022-03-01T02:15:30.917+0530","objectType":"Question","primaryCategory":"Multiple Choice question","contentDisposition":"inline","lastUpdatedOn":"2022-03-01T02:15:30.915+0530","contentEncoding":"gzip","showSolutions":"No","allowAnonymousAccess":"Yes","identifier":"do_11348469662446387212","lastStatusChangedOn":"2022-03-01T02:15:30.917+0530","visibility":"Parent","showTimer":"No","index":1,"languageCode":["en"],"version":1,"versionKey":"1646081131087","showFeedback":"No","license":"CC BY 4.0","depth":1,"createdBy":"g-001","compatibilityLevel":4,"name":"Q1","status":"Draft"},{"parent":"do_11348469558523494411","code":"Q2","description":"Q2","language":["English"],"mimeType":"application/vnd.sunbird.question","createdOn":"2022-03-01T02:15:31.113+0530","objectType":"Question","primaryCategory":"Multiple Choice question","contentDisposition":"inline","lastUpdatedOn":"2022-03-01T02:15:31.126+0530","contentEncoding":"gzip","showSolutions":"No","allowAnonymousAccess":"Yes","identifier":"do_11348469662607769614","lastStatusChangedOn":"2022-03-01T02:15:31.113+0530","visibility":"Default","showTimer":"No","index":2,"languageCode":["en"],"version":1,"versionKey":"1646081131126","showFeedback":"No","license":"CC BY 4.0","depth":1,"createdBy":"g-002","compatibilityLevel":4,"name":"Q2","status":"Draft"}]}"""
val response = new Response
response.put("hierarchy", hierarchyString)
}

def getEmptyCassandraHierarchy(): Response = {
val response = new Response
response.put("hierarchy", "{}")
Expand Down
Loading

0 comments on commit 39eac28

Please sign in to comment.