Skip to content

Commit

Permalink
Reduce the dependency of Lecture onto Play
Browse files Browse the repository at this point in the history
We still depend on play.api.libs.json, and this should be replaced by
another library, such as the one already used in PLM.

The ultimate goal is to move the lessons to their own repository, so
that they can be properly shared between the judge, the server and the
javaUI (when revived).

The current version is not satisfactory because the exercises are
serialized in json between the server and the judge. That's really
inefficient, and even error prone. Right now, asking the judge to
deserialize a turtle world freezes it (pb to be investiguated).

The exercises cannot move back into PLM itself (nor in a fixed jar)
because at some point we want to revive the online editor:
BuggleInc/PLM#453
The dream is that each teacher can assemble a lesson from existing
resources.  This will probably create forks on lessons and exercises,
but if the material is in a git, that's no big deal. Then, the teacher
points the pupils onto the exact commit of the lesson, and every parts
get the material from the git, in the right version using the git hash.
  • Loading branch information
mquinson committed Oct 1, 2017
1 parent 0cca4f8 commit 1e91cd1
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 32 deletions.
17 changes: 11 additions & 6 deletions app/actors/PLMActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ class PLMActor(pushActor: ActorRef,

registerActor

private def checkExercisePassed(exercise: Exercise, progLang: ProgrammingLanguage): Future[Boolean] =
(sessionActor ? SessionActor.IsExercisePassed(exercise, progLang)).mapTo[Boolean]

def receive = {
case msg: JsValue =>
Logger.debug("Received a message")
Expand Down Expand Up @@ -142,7 +145,7 @@ class PLMActor(pushActor: ActorRef,
gitActor ! SwitchUser(currentGitID, None)
case "getLessons" =>
val jsonLessons: JsArray =
Lesson.arrayToJson(lessons.lessonsList, currentHumanLang)
Lesson.arrayToJson(lessons.lessonsList, currentHumanLang.code)
sendMessage("lessons", Json.obj(
"lessons" -> jsonLessons
))
Expand All @@ -152,9 +155,10 @@ class PLMActor(pushActor: ActorRef,
case Some(lessonName: String) =>
val jsonLectures: JsArray =
Lecture.arrayToJson(
sessionActor,
Logger.logger,
checkExercisePassed,
lessons.exercisesList(lessonName),
currentHumanLang,
currentHumanLang.code,
currentProgLang,
exercises)
sendMessage("lectures", Json.obj(
Expand Down Expand Up @@ -579,7 +583,7 @@ class PLMActor(pushActor: ActorRef,

def generateUpdatedLessonsListJson(): JsValue = {
Json.obj(
"lessons" -> Lesson.arrayToJson(lessons.lessonsList, currentHumanLang)
"lessons" -> Lesson.arrayToJson(lessons.lessonsList, currentHumanLang.code)
)
}

Expand All @@ -589,9 +593,10 @@ class PLMActor(pushActor: ActorRef,
Json.obj(
"lectures" ->
Lecture.arrayToJson(
sessionActor,
Logger.logger,
checkExercisePassed,
lessons.exercisesList(lessonName),
currentHumanLang,
currentHumanLang.code,
currentProgLang,
exercises))
case _ => Json.obj()
Expand Down
32 changes: 14 additions & 18 deletions app/models/lesson/Lecture.scala
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
package models.lesson

import actors.SessionActor
import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import play.api.Logger
import play.api.i18n.Lang
import models.lesson.Lecture.ExercisePassedChecker
import org.slf4j.Logger
import play.api.libs.functional.syntax._
import play.api.libs.json._
import plm.core.lang.ProgrammingLanguage
import plm.core.model.lesson.Exercise
import plm.core.ui.PlmHtmlEditorKit

import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
import scala.language.postfixOps

/**
* @author matthieu
*/
object Lecture {

type ExercisePassedChecker = (Exercise, ProgrammingLanguage) => Future[Boolean]

implicit lazy val lectureReads: Reads[Lecture] = (
(JsPath \ "id").read[String] and
(JsPath \ "names").readNullable[Map[String, String]] and
(JsPath \ "dependingLectures").lazyRead(Reads.seq[Lecture](lectureReads))
)(Lecture.apply _)

def arrayToJson(sessionActor: ActorRef, lectures: Array[Lecture], lang: Lang, progLang: ProgrammingLanguage, exercises: Exercises): JsArray = {
def arrayToJson(logger: Logger, checkExercisePassed: ExercisePassedChecker, lectures: Array[Lecture], languageCode: String, progLang: ProgrammingLanguage, exercises: Exercises): JsArray = {
var jsonLectures: JsArray = Json.arr()
lectures.foreach { lecture: Lecture =>
jsonLectures = jsonLectures.append(lecture.toJson(sessionActor, lang, progLang, exercises))
jsonLectures = jsonLectures.append(lecture.toJson(logger, checkExercisePassed, languageCode, progLang, exercises))
}
jsonLectures
}
Expand All @@ -50,36 +48,34 @@ case class Lecture(id: String, optNames: Option[Map[String, String]], dependingL
array
}

def toJson(sessionActor: ActorRef, lang: Lang, progLang: ProgrammingLanguage, exercises: Exercises): JsObject = {
def toJson(logger: Logger, checkedExercisePassed: ExercisePassedChecker, languageCode: String, progLang: ProgrammingLanguage, exercises: Exercises): JsObject = {
val names: Map[String, String] = optNames.get
val defaultName: String = names.get("en").get
val name: String = names.getOrElse(lang.code, defaultName)
val name: String = names.getOrElse(languageCode, defaultName)

val exercisePassed: Map[String, Boolean] = generateExercisePassed(sessionActor, exercises)
val exercisePassed: Map[String, Boolean] = generateExercisePassed(logger, checkedExercisePassed, exercises)

Json.obj(
"id" -> id,
"name" -> PlmHtmlEditorKit.filterHTML(name, false, progLang),
"dependingLectures" -> Lecture.arrayToJson(sessionActor, dependingLectures.toArray, lang, progLang, exercises),
"dependingLectures" -> Lecture.arrayToJson(logger, checkedExercisePassed, dependingLectures.toArray, languageCode, progLang, exercises),
"exercisePassed" -> exercisePassed
)
}

def generateExercisePassed(sessionActor: ActorRef, exercises: Exercises): Map[String, Boolean] = {
implicit val timeout = Timeout(5 seconds)

def generateExercisePassed(logger: Logger, checkedExercisePassed: ExercisePassedChecker, exercises: Exercises): Map[String, Boolean] = {
var exercisePassed: Map[String, Boolean] = Map()

exercises.getExercise(id) match {
case Some(exercise: Exercise) =>
exercise.getProgLanguages.toArray(Array[ProgrammingLanguage]()).foreach { supportedProgLang =>
val future = (sessionActor ? SessionActor.IsExercisePassed(exercise, supportedProgLang)).mapTo[Boolean].map { passed =>
val future = checkedExercisePassed(exercise, supportedProgLang).map { passed =>
exercisePassed = exercisePassed + (supportedProgLang.getLang -> passed)
}
Await.result(future, 5 seconds)
}
case None =>
Logger.info("Did not find following exercise: " + id)
logger.info("Did not find following exercise: " + id)
}
exercisePassed.toMap
}
Expand Down
9 changes: 4 additions & 5 deletions app/models/lesson/Lesson.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package models.lesson

import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.i18n.Lang

/**
* @author matthieu
Expand All @@ -14,10 +13,10 @@ object Lesson {
(JsPath \ "lectures").read[Array[Lecture]]
)(Lesson.apply _)

def arrayToJson(lessons: Array[Lesson], humanLang: Lang): JsArray = {
def arrayToJson(lessons: Array[Lesson], humanLanguageCode: String): JsArray = {
var jsonLessons: JsArray = Json.arr()
lessons.foreach { lesson: Lesson =>
jsonLessons = jsonLessons.append(lesson.toJson(humanLang))
jsonLessons = jsonLessons.append(lesson.toJson(humanLanguageCode))
}
jsonLessons
}
Expand All @@ -43,11 +42,11 @@ case class Lesson(id: String, name: Option[String], lectures: Array[Lecture]) {
orderedIDs.contains(exerciseID)
}

def toJson(lang: Lang): JsObject = {
def toJson(languageCode: String): JsObject = {
val imgPath: String = "lessons/" + id.replaceAll("\\.", "/") + "/icon.png"
val descriptions: Map[String, String] = optDescriptions.get
val defaultDescription: String = descriptions.get("en").get
val description: String = descriptions.getOrElse(lang.code, defaultDescription)
val description: String = descriptions.getOrElse(languageCode, defaultDescription)

Json.obj(
"id" -> id,
Expand Down
4 changes: 2 additions & 2 deletions app/models/lesson/TipFactory.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package models.lesson

import plm.core.model.lesson.tip.AbstractTipFactory
import java.util.Locale
import plm.core.model.I18nManager

import plm.core.model.lesson.tip.AbstractTipFactory

class TipFactory extends AbstractTipFactory {

Expand Down
2 changes: 1 addition & 1 deletion deps/PLM

0 comments on commit 1e91cd1

Please sign in to comment.