diff --git a/.gitignore b/.gitignore index 86daf73..9f0492f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,11 @@ .settings/ console.devmode.log develop.db.mv.db +develop.db.trace.db project/project/ project/target/ target/ src/main/resources/props/default.props src/main/resources/props/production.default.props +.idea +/.gitignore diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index d1132b2..ab524de 100644 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -40,10 +40,16 @@ class Boot { Schemifier.schemify(true, Schemifier.infoF _, User, Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType, DFAConstructionProblem, DFAConstructionSolutionAttempt, + ProductConstructionProblem, ProductConstructionSolutionAttempt, + MinimizationProblem, MinimizationSolutionAttempt, NFAConstructionProblem, NFAConstructionSolutionAttempt, NFAToDFAProblem, NFAToDFASolutionAttempt, RegExConstructionProblem, RegexConstructionSolutionAttempt, PumpingLemmaProblem, PumpingLemmaSolutionAttempt, + WordsInGrammarProblem, WordsInGrammarSolutionAttempt, + GrammarToCNFProblem, GrammarToCNFSolutionAttempt, + DescriptionToGrammarProblem, DescriptionToGrammarSolutionAttempt, + CYKProblem, CYKSolutionAttempt, ProblemSet, Role, SolutionAttempt, Supervision) StartupHooks.hooks map (hook => hook()) @@ -62,7 +68,7 @@ class Boot { Menu.i("Practice Problem Sets") / "practicesets" / "index" >> loggedInPredicate submenus( Menu.i("Solve Practice Set Problem") /"practicesets" / "solve" >> Hidden), - + Menu.i("Problems") / "problems" / "index" >> isInstructorPredicate submenus( Menu.i("Create Problem") / "problems" / "create" >> Hidden, Menu.i("Edit Problem") / "problems" / "edit" >> Hidden), diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala index 519c33f..78786de 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -118,6 +118,47 @@ object GraderConnection { return ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li")) } + + //Product Construction + + def getProductConstructionFeedback(correctDfaDescriptionList : List[String], attemptDfaDescription : String, booleanOperation : String, maxGrade : Int) : (Int, NodeSeq) = { + + def stringListToNodeList(xs: List[String]): List[Node] = xs match{ + case Nil => List() + case y :: ys => Elem(null, "dfaDesc", Null, TopScope, true, XML.loadString(y)) :: stringListToNodeList(ys) + } + + val arguments = Map[String, Node]( + "dfaDescList" -> Elem(null, "dfaDescList", Null, TopScope, true, stringListToNodeList(correctDfaDescriptionList):_*), + "dfaAttemptDesc" -> XML.loadString(attemptDfaDescription), + "booleanOperation" -> Elem(null, "booleanOperation", Null, TopScope, true, Text(booleanOperation)), + "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)), + "feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")), + "enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored"))) + + val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackProductConstruction", arguments) + + ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li")) + } + + // Minimization + + //TODO: Pass Minimization Table to variable 'minimizationTableAttempt' + def getMinimizationFeedback(dfaDescription : String, minimizationTableDescription : String, attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = { + + val arguments = Map[String, Node]( + "dfaDesc" -> XML.loadString(dfaDescription), + "minimizationTableAttempt" -> XML.loadString(minimizationTableDescription), + "dfaAttemptDesc" -> XML.loadString(attemptDfaDescription), + "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)), + "feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")), + "enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored"))); + + //TODO: Implement 'ComputeFeedbackMinimization' in Backend + val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackMinimization", arguments) + + ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li")) + } // NFA @@ -175,6 +216,84 @@ object GraderConnection { if(responseXml.text.equals("CorrectRegex")) return List() else return List(responseXml.text) } + //Grammar + def getGrammarParsingErrors(potentialGrammar : String) : Seq[String] = { + val arguments = Map[String, Node]( + "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(potentialGrammar)) + ) + + val responseXml = soapConnection.callMethod(namespace, "CheckGrammar", arguments) + + if(responseXml.text.equals("CorrectGrammar")) return List() else return List(responseXml.text) + } + + def getCNFParsingErrors(potentialGrammar : String) : Seq[String] = { + val arguments = Map[String, Node]( + "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(potentialGrammar)) + ) + + val responseXml = soapConnection.callMethod(namespace, "isCNF", arguments) + + if((responseXml \ "res").head.text.equals("y")) return List() else return List((responseXml \ "feedback").head.text) + } + + def getWordsInGrammarFeedback(grammar: String, wordsIn : Seq[String], wordsOut : Seq[String], maxGrade : Int) : (Int, NodeSeq) = { + val arguments = Map[String, Node]( + "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(grammar)), + "wordsIn" ->
{ wordsIn.map((symbol : String) => Elem(null, "word", Null, TopScope, true, Text(symbol))) }
, + "wordsOut" ->
{ wordsOut.map((symbol : String) => Elem(null, "word", Null, TopScope, true, Text(symbol))) }
, + "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)) + ); + + val responseXml = soapConnection.callMethod(namespace, "ComputeWordsInGrammarFeedback", arguments) + + return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback")) + } + + def getDescriptionToGrammarFeedback(solution: String, attempt: String, maxGrade : Int) : (Int, NodeSeq) = { + val arguments = Map[String, Node]( + "solution" -> Elem(null, "solution", Null, TopScope, true, Text(solution)), + "attempt" -> Elem(null, "attempt", Null, TopScope, true, Text(attempt)), + "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)), + "checkEmptyWord" -> Elem(null, "checkEmptyWord", Null, TopScope, true, Text(true.toString)) + ); + + val responseXml = soapConnection.callMethod(namespace, "ComputeGrammarEqualityFeedback", arguments) + + return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback")) + } + + def getGrammarToCNFFeedback(solution: String, attempt: String, maxGrade : Int) : (Int, NodeSeq) = { + //check that attempt is in CNF + val arguments1 = Map[String, Node]( + "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(attempt)) + ); + val responseXml1 = soapConnection.callMethod(namespace, "isCNF", arguments1) + if ((responseXml1 \ "res").head.text == "n") return (-1, (responseXml1 \ "feedback")) + + val arguments2 = Map[String, Node]( + "solution" -> Elem(null, "solution", Null, TopScope, true, Text(solution)), + "attempt" -> Elem(null, "attempt", Null, TopScope, true, Text(attempt)), + "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)), + "checkEmptyWord" -> Elem(null, "checkEmptyWord", Null, TopScope, true, Text(false.toString)) + ); + val responseXml2 = soapConnection.callMethod(namespace, "ComputeGrammarEqualityFeedback", arguments2) + return ((responseXml2 \ "grade").head.text.toInt, (responseXml2 \ "feedback")) + } + + def getCYKFeedback(grammar: String, word: String, cyk_attempt: String, maxGrade : Int) : (Int, NodeSeq) = { + val arguments = Map[String, Node]( + "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(grammar)), + "word" -> Elem(null, "word", Null, TopScope, true, Text(word)), + "attempt" -> XML.loadString(cyk_attempt), + "maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)) + ); + + val responseXml = soapConnection.callMethod(namespace, "ComputeCYKFeedback", arguments) + + return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback")) + } + // Pumping lemma def getPLParsingErrors(languageDesc : String, constraintDesc : String, diff --git a/src/main/scala/com/automatatutor/model/CYKProblem.scala b/src/main/scala/com/automatatutor/model/CYKProblem.scala new file mode 100644 index 0000000..a554fa5 --- /dev/null +++ b/src/main/scala/com/automatatutor/model/CYKProblem.scala @@ -0,0 +1,45 @@ +package com.automatatutor.model + +import scala.xml.NodeSeq +import scala.xml.XML +import net.liftweb.mapper.By +import net.liftweb.mapper.IdPK +import net.liftweb.mapper.LongKeyedMapper +import net.liftweb.mapper.LongKeyedMetaMapper +import net.liftweb.mapper.MappedString +import net.liftweb.mapper.MappedText +import net.liftweb.mapper.MappedInt +import net.liftweb.mapper.MappedLongForeignKey +import bootstrap.liftweb.StartupHook + +class CYKProblem extends LongKeyedMapper[CYKProblem] with IdPK with SpecificProblem[CYKProblem] { + def getSingleton = CYKProblem + + object problemId extends MappedLongForeignKey(this, Problem) + object grammar extends MappedText(this) + object word extends MappedText(this) + + def getGrammar = this.grammar.is + def setGrammar(g : String) = this.grammar(g) + def getWord = this.word.is + def setWord(w : String) = this.word(w) + + override def copy(): CYKProblem = { + val retVal = new CYKProblem + retVal.problemId(this.problemId.get) + retVal.grammar(this.grammar.get) + retVal.word(this.word.get) + return retVal + } + + override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem) + +} + +object CYKProblem extends CYKProblem with LongKeyedMetaMapper[CYKProblem] { + def findByGeneralProblem(generalProblem : Problem) : CYKProblem = + find(By(CYKProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a CYKProblem") + + def deleteByGeneralProblem(generalProblem : Problem) : Boolean = + this.bulkDelete_!!(By(CYKProblem.problemId, generalProblem)) +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/model/DescriptionToGrammarProblem.scala b/src/main/scala/com/automatatutor/model/DescriptionToGrammarProblem.scala new file mode 100644 index 0000000..72cd20b --- /dev/null +++ b/src/main/scala/com/automatatutor/model/DescriptionToGrammarProblem.scala @@ -0,0 +1,41 @@ +package com.automatatutor.model + +import scala.xml.NodeSeq +import scala.xml.XML +import net.liftweb.mapper.By +import net.liftweb.mapper.IdPK +import net.liftweb.mapper.LongKeyedMapper +import net.liftweb.mapper.LongKeyedMetaMapper +import net.liftweb.mapper.MappedString +import net.liftweb.mapper.MappedText +import net.liftweb.mapper.MappedInt +import net.liftweb.mapper.MappedLongForeignKey +import bootstrap.liftweb.StartupHook + +class DescriptionToGrammarProblem extends LongKeyedMapper[DescriptionToGrammarProblem] with IdPK with SpecificProblem[DescriptionToGrammarProblem] { + def getSingleton = DescriptionToGrammarProblem + + object problemId extends MappedLongForeignKey(this, Problem) + object grammar extends MappedText(this) + + def getGrammar = this.grammar.is + def setGrammar(g : String) = this.grammar(g) + + override def copy(): DescriptionToGrammarProblem = { + val retVal = new DescriptionToGrammarProblem + retVal.problemId(this.problemId.get) + retVal.grammar(this.grammar.get) + return retVal + } + + override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem) + +} + +object DescriptionToGrammarProblem extends DescriptionToGrammarProblem with LongKeyedMetaMapper[DescriptionToGrammarProblem] { + def findByGeneralProblem(generalProblem : Problem) : DescriptionToGrammarProblem = + find(By(DescriptionToGrammarProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a DescriptionToGrammarProblem") + + def deleteByGeneralProblem(generalProblem : Problem) : Boolean = + this.bulkDelete_!!(By(DescriptionToGrammarProblem.problemId, generalProblem)) +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala b/src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala new file mode 100644 index 0000000..6e31c74 --- /dev/null +++ b/src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala @@ -0,0 +1,41 @@ +package com.automatatutor.model + +import scala.xml.NodeSeq +import scala.xml.XML +import net.liftweb.mapper.By +import net.liftweb.mapper.IdPK +import net.liftweb.mapper.LongKeyedMapper +import net.liftweb.mapper.LongKeyedMetaMapper +import net.liftweb.mapper.MappedString +import net.liftweb.mapper.MappedText +import net.liftweb.mapper.MappedInt +import net.liftweb.mapper.MappedLongForeignKey +import bootstrap.liftweb.StartupHook + +class GrammarToCNFProblem extends LongKeyedMapper[GrammarToCNFProblem] with IdPK with SpecificProblem[GrammarToCNFProblem] { + def getSingleton = GrammarToCNFProblem + + object problemId extends MappedLongForeignKey(this, Problem) + object grammar extends MappedText(this) + + def getGrammar = this.grammar.is + def setGrammar(g : String) = this.grammar(g) + + override def copy(): GrammarToCNFProblem = { + val retVal = new GrammarToCNFProblem + retVal.problemId(this.problemId.get) + retVal.grammar(this.grammar.get) + return retVal + } + + override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem) + +} + +object GrammarToCNFProblem extends GrammarToCNFProblem with LongKeyedMetaMapper[GrammarToCNFProblem] { + def findByGeneralProblem(generalProblem : Problem) : GrammarToCNFProblem = + find(By(GrammarToCNFProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a GrammarToCNFProblem") + + def deleteByGeneralProblem(generalProblem : Problem) : Boolean = + this.bulkDelete_!!(By(GrammarToCNFProblem.problemId, generalProblem)) +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/model/MinimizationProblem.scala b/src/main/scala/com/automatatutor/model/MinimizationProblem.scala new file mode 100644 index 0000000..47556bc --- /dev/null +++ b/src/main/scala/com/automatatutor/model/MinimizationProblem.scala @@ -0,0 +1,49 @@ +package com.automatatutor.model + +import net.liftweb.mapper.MappedString +import net.liftweb.mapper.LongKeyedMapper +import net.liftweb.mapper.LongKeyedMetaMapper +import net.liftweb.mapper.MappedLongForeignKey +import net.liftweb.mapper.IdPK +import net.liftweb.mapper.By +import net.liftweb.mapper.MappedText + +import scala.xml.XML +import scala.xml.NodeSeq +import bootstrap.liftweb.StartupHook + +class MinimizationProblem extends LongKeyedMapper[MinimizationProblem] with IdPK with SpecificProblem[MinimizationProblem] { + def getSingleton = MinimizationProblem + + protected object problemId extends MappedLongForeignKey(this, Problem) + protected class automatonClass extends MappedText(this) { + + } + protected val automaton: automatonClass = new automatonClass + + def getGeneralProblem = this.problemId.obj openOrThrowException "Every MinimizationProblem must have a ProblemId" + override def setGeneralProblem(problem : Problem) : MinimizationProblem = this.problemId(problem) + + def getAutomaton = this.automaton.is + def setAutomaton(automaton : String) = this.automaton(automaton) + def setAutomaton(automaton : NodeSeq) = this.automaton(automaton.mkString) + + def getXmlDescription : NodeSeq = XML.loadString(this.automaton.is) + + def getAlphabet : Seq[String] = (getXmlDescription \ "alphabet" \ "symbol").map(_.text) + + override def copy(): MinimizationProblem = { + val retVal = new MinimizationProblem + retVal.problemId(this.problemId.get) + retVal.automaton(this.automaton.get) + return retVal + } +} + +object MinimizationProblem extends MinimizationProblem with LongKeyedMetaMapper[MinimizationProblem] { + def findByGeneralProblem(generalProblem : Problem) : MinimizationProblem = + find(By(MinimizationProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a MinimizationProblem") + + def deleteByGeneralProblem(generalProblem : Problem) : Boolean = + bulkDelete_!!(By(MinimizationProblem.problemId, generalProblem)) +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/model/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala index 0571712..ee714b0 100644 --- a/src/main/scala/com/automatatutor/model/Problem.scala +++ b/src/main/scala/com/automatatutor/model/Problem.scala @@ -5,8 +5,12 @@ import com.automatatutor.snippet.NFAProblemSnippet import com.automatatutor.snippet.NFAToDFAProblemSnippet import com.automatatutor.snippet.ProblemSnippet import com.automatatutor.snippet.RegExConstructionSnippet -import com.automatatutor.snippet.PumpingLemmaProblemSnippet -import net.liftweb.common.Empty +import com.automatatutor.snippet.WordsInGrammarSnippet +import com.automatatutor.snippet.DescriptionToGrammarSnippet +import com.automatatutor.snippet.CYKProblemSnippet +import com.automatatutor.snippet.GrammarToCNFSnippet +import com.automatatutor.snippet.MinimizationSnippet +import com.automatatutor.snippet.ProductConstructionSnippet import net.liftweb.common.Full import net.liftweb.mapper.By import net.liftweb.mapper.IdPK @@ -27,14 +31,26 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { val NFAConstructionTypeName = "English to NFA" val NFAToDFATypeName = "NFA to DFA" val EnglishToRegExTypeName = "English to Regular Expression" - //val PLTypeName = "Pumping Lemma Proof" + val PLTypeName = "Pumping Lemma Proof" val BuchiSolvingTypeName = "Buchi Game Solving" + val WordsInGrammarTypeName = "Words in Grammar" + val DescriptionToGrammarTypeName = "English to Grammar" + val GrammarToCNFTypeName = "Grammar to CNF" + val CYKTypeName = "CYK Algorithm" + val ProductConstructionTypeName = "Product Construction" + val MinimizationTypeName = "Minimization" val knownProblemTypes : Map[String, ProblemSnippet] = Map( DFAConstructionTypeName -> DFAConstructionSnippet, NFAConstructionTypeName -> NFAProblemSnippet, NFAToDFATypeName -> NFAToDFAProblemSnippet, - EnglishToRegExTypeName -> RegExConstructionSnippet + EnglishToRegExTypeName -> RegExConstructionSnippet, + WordsInGrammarTypeName -> WordsInGrammarSnippet, + DescriptionToGrammarTypeName -> DescriptionToGrammarSnippet, + GrammarToCNFTypeName -> GrammarToCNFSnippet, + CYKTypeName -> CYKProblemSnippet, + ProductConstructionTypeName -> ProductConstructionSnippet, + MinimizationTypeName -> MinimizationSnippet ) ++ (if(Config.buchiGameSolving.enabled.get) { Map(BuchiSolvingTypeName -> BuchiGameSolving.SnippetAdapter) } else { Map[String, ProblemSnippet]() }) @@ -47,9 +63,15 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { def getSpecificProblem(generalProblem: Problem): SpecificProblem[_] = this.problemTypeName.get match { case DFAConstructionTypeName => DFAConstructionProblem.findByGeneralProblem(generalProblem) + case ProductConstructionTypeName => ProductConstructionProblem.findByGeneralProblem(generalProblem) + case MinimizationTypeName => MinimizationProblem.findByGeneralProblem(generalProblem) case NFAConstructionTypeName => NFAConstructionProblem.findByGeneralProblem(generalProblem) case NFAToDFATypeName => NFAToDFAProblem.findByGeneralProblem(generalProblem) case EnglishToRegExTypeName => RegExConstructionProblem.findByGeneralProblem(generalProblem) + case WordsInGrammarTypeName => WordsInGrammarProblem.findByGeneralProblem(generalProblem) + case DescriptionToGrammarTypeName => DescriptionToGrammarProblem.findByGeneralProblem(generalProblem) + case GrammarToCNFTypeName => GrammarToCNFProblem.findByGeneralProblem(generalProblem) + case CYKTypeName => CYKProblem.findByGeneralProblem(generalProblem) } } diff --git a/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala b/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala new file mode 100644 index 0000000..a7bc9ac --- /dev/null +++ b/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala @@ -0,0 +1,59 @@ +package com.automatatutor.model + +import net.liftweb.mapper.MappedString +import net.liftweb.mapper.LongKeyedMapper +import net.liftweb.mapper.LongKeyedMetaMapper +import net.liftweb.mapper.MappedLongForeignKey +import net.liftweb.mapper.IdPK +import net.liftweb.mapper.By +import net.liftweb.mapper.MappedText +import scala.xml.XML +import scala.xml.NodeSeq +import bootstrap.liftweb.StartupHook + +class ProductConstructionProblem extends LongKeyedMapper[ProductConstructionProblem] with IdPK with SpecificProblem[ProductConstructionProblem] { + def getSingleton = ProductConstructionProblem + + protected object problemId extends MappedLongForeignKey(this, Problem) + protected class automatonClass extends MappedText(this) { + + } + protected class booleanOperationClass extends MappedText(this) { + + } + protected val automaton1 : automatonClass = new automatonClass + protected val automaton2 : automatonClass = new automatonClass + protected val automataList : List[automatonClass] = List(automaton1, automaton2) + protected val booleanOperation : booleanOperationClass = new booleanOperationClass + + def getGeneralProblem = this.problemId.obj openOrThrowException "Every ProductConstructionProblem must have a ProblemId" + override def setGeneralProblem(problem : Problem) : ProductConstructionProblem = this.problemId(problem) + + def getAutomataList = this.automataList + def getBooleanOperation = this.booleanOperation + def setAutomaton1(automaton : String) = this.automataList(0)(automaton) + def setAutomaton2(automaton : String) = this.automataList(1)(automaton) + def setBooleanOperation(boolOp : String) = this.booleanOperation(boolOp) + def setAutomaton1(automaton : NodeSeq) = this.automataList(0)(automaton.mkString) + def setAutomaton2(automaton : NodeSeq) = this.automataList(1)(automaton.mkString) + + def getXmlDescription1 : NodeSeq = XML.loadString(this.automataList(0).is) + def getXmlDescription2 : NodeSeq = XML.loadString(this.automataList(1).is) + + def getAlphabet : Seq[String] = (getXmlDescription1 \ "alphabet" \ "symbol").map(_.text) + + override def copy(): ProductConstructionProblem = { + val retVal = new ProductConstructionProblem + retVal.problemId(this.problemId.get) + retVal.automataList(0)(this.automataList(0).get) + return retVal + } +} + +object ProductConstructionProblem extends ProductConstructionProblem with LongKeyedMetaMapper[ProductConstructionProblem] { + def findByGeneralProblem(generalProblem : Problem) : ProductConstructionProblem = + find(By(ProductConstructionProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a ProductConstructionProblem") + + def deleteByGeneralProblem(generalProblem : Problem) : Boolean = + bulkDelete_!!(By(ProductConstructionProblem.problemId, generalProblem)) +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala index 0dc4f2d..5175950 100644 --- a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala +++ b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala @@ -22,6 +22,7 @@ object SolutionAttempt extends SolutionAttempt with LongKeyedMetaMapper[Solution } } +//DFA Construction class DFAConstructionSolutionAttempt extends LongKeyedMapper[DFAConstructionSolutionAttempt] with IdPK { def getSingleton = DFAConstructionSolutionAttempt @@ -31,10 +32,39 @@ class DFAConstructionSolutionAttempt extends LongKeyedMapper[DFAConstructionSolu object DFAConstructionSolutionAttempt extends DFAConstructionSolutionAttempt with LongKeyedMetaMapper[DFAConstructionSolutionAttempt] { def getByGeneralAttempt ( generalAttempt : SolutionAttempt ) : DFAConstructionSolutionAttempt = { - return this.find(By(DFAConstructionSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a DFA construction attempt" + return this.find(By(DFAConstructionSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a DFA construction attempt" } } +//Product Construction +class ProductConstructionSolutionAttempt extends LongKeyedMapper[ProductConstructionSolutionAttempt] with IdPK { + def getSingleton = ProductConstructionSolutionAttempt + + object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt) + object attemptAutomaton extends MappedText(this) +} + +object ProductConstructionSolutionAttempt extends ProductConstructionSolutionAttempt with LongKeyedMetaMapper[ProductConstructionSolutionAttempt] { + def getByGeneralAttempt ( generalAttempt : SolutionAttempt ) : ProductConstructionSolutionAttempt = { + return this.find(By(ProductConstructionSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a product construction attempt" + } +} + +//Minimization +class MinimizationSolutionAttempt extends LongKeyedMapper[MinimizationSolutionAttempt] with IdPK { + def getSingleton = MinimizationSolutionAttempt + + object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt) + object attemptAutomaton extends MappedText(this) +} + +object MinimizationSolutionAttempt extends MinimizationSolutionAttempt with LongKeyedMetaMapper[MinimizationSolutionAttempt] { + def getByGeneralAttempt ( generalAttempt : SolutionAttempt ) : MinimizationSolutionAttempt = { + return this.find(By(MinimizationSolutionAttempt.solutionAttemptId, generalAttempt)) openOrThrowException "Must only be called if we are sure that the general attempt also has a minimization attempt" + } +} + +//NFA Construction class NFAConstructionSolutionAttempt extends LongKeyedMapper[NFAConstructionSolutionAttempt] with IdPK { def getSingleton = NFAConstructionSolutionAttempt @@ -46,6 +76,7 @@ object NFAConstructionSolutionAttempt extends NFAConstructionSolutionAttempt wit } +//NFA to DFA class NFAToDFASolutionAttempt extends LongKeyedMapper[NFAToDFASolutionAttempt] with IdPK { def getSingleton = NFAToDFASolutionAttempt @@ -57,6 +88,7 @@ object NFAToDFASolutionAttempt extends NFAToDFASolutionAttempt with LongKeyedMet } +//Regex Construction class RegexConstructionSolutionAttempt extends LongKeyedMapper[RegexConstructionSolutionAttempt] with IdPK { def getSingleton = RegexConstructionSolutionAttempt @@ -68,6 +100,52 @@ object RegexConstructionSolutionAttempt extends RegexConstructionSolutionAttempt } +//Pumping Lemma +class WordsInGrammarSolutionAttempt extends LongKeyedMapper[WordsInGrammarSolutionAttempt] with IdPK { + def getSingleton = WordsInGrammarSolutionAttempt + + object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt) + object attemptWordsIn extends MappedText(this) + object attemptWordsOut extends MappedText(this) +} + +object WordsInGrammarSolutionAttempt extends WordsInGrammarSolutionAttempt with LongKeyedMetaMapper[WordsInGrammarSolutionAttempt] { + +} + +class DescriptionToGrammarSolutionAttempt extends LongKeyedMapper[DescriptionToGrammarSolutionAttempt] with IdPK { + def getSingleton = DescriptionToGrammarSolutionAttempt + + object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt) + object attemptGrammar extends MappedText(this) +} + +object DescriptionToGrammarSolutionAttempt extends DescriptionToGrammarSolutionAttempt with LongKeyedMetaMapper[DescriptionToGrammarSolutionAttempt] { + +} + +class GrammarToCNFSolutionAttempt extends LongKeyedMapper[GrammarToCNFSolutionAttempt] with IdPK { + def getSingleton = GrammarToCNFSolutionAttempt + + object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt) + object attemptGrammar extends MappedText(this) +} + +object GrammarToCNFSolutionAttempt extends GrammarToCNFSolutionAttempt with LongKeyedMetaMapper[GrammarToCNFSolutionAttempt] { + +} + +class CYKSolutionAttempt extends LongKeyedMapper[CYKSolutionAttempt] with IdPK { + def getSingleton = CYKSolutionAttempt + + object solutionAttemptId extends MappedLongForeignKey(this, SolutionAttempt) + object attempt extends MappedText(this) +} + +object CYKSolutionAttempt extends CYKSolutionAttempt with LongKeyedMetaMapper[CYKSolutionAttempt] { + +} + class PumpingLemmaSolutionAttempt extends LongKeyedMapper[PumpingLemmaSolutionAttempt] with IdPK { def getSingleton = PumpingLemmaSolutionAttempt diff --git a/src/main/scala/com/automatatutor/model/WordsInGrammarProblem.scala b/src/main/scala/com/automatatutor/model/WordsInGrammarProblem.scala new file mode 100644 index 0000000..c1cf78e --- /dev/null +++ b/src/main/scala/com/automatatutor/model/WordsInGrammarProblem.scala @@ -0,0 +1,49 @@ +package com.automatatutor.model + +import scala.xml.NodeSeq +import scala.xml.XML +import net.liftweb.mapper.By +import net.liftweb.mapper.IdPK +import net.liftweb.mapper.LongKeyedMapper +import net.liftweb.mapper.LongKeyedMetaMapper +import net.liftweb.mapper.MappedString +import net.liftweb.mapper.MappedText +import net.liftweb.mapper.MappedInt +import net.liftweb.mapper.MappedLongForeignKey +import bootstrap.liftweb.StartupHook + +class WordsInGrammarProblem extends LongKeyedMapper[WordsInGrammarProblem] with IdPK with SpecificProblem[WordsInGrammarProblem] { + def getSingleton = WordsInGrammarProblem + + object problemId extends MappedLongForeignKey(this, Problem) + object grammar extends MappedText(this) + object inNeeded extends MappedInt(this) + object outNeeded extends MappedInt(this) + + def getGrammar = this.grammar.is + def setGrammar(g : String) = this.grammar(g) + def getInNeeded = this.inNeeded.is + def setInNeeded(i : Int) = this.inNeeded(i) + def getOutNeeded = this.outNeeded.is + def setOutNeeded(i : Int) = this.outNeeded(i) + + override def copy(): WordsInGrammarProblem = { + val retVal = new WordsInGrammarProblem + retVal.problemId(this.problemId.get) + retVal.grammar(this.grammar.get) + retVal.inNeeded(this.inNeeded.get) + retVal.outNeeded(this.outNeeded.get) + return retVal + } + + override def setGeneralProblem(newProblem: Problem) = this.problemId(newProblem) + +} + +object WordsInGrammarProblem extends WordsInGrammarProblem with LongKeyedMetaMapper[WordsInGrammarProblem] { + def findByGeneralProblem(generalProblem : Problem) : WordsInGrammarProblem = + find(By(WordsInGrammarProblem.problemId, generalProblem)) openOrThrowException("Must only be called if we are sure that generalProblem is a WordsInGrammarProblem") + + def deleteByGeneralProblem(generalProblem : Problem) : Boolean = + this.bulkDelete_!!(By(WordsInGrammarProblem.problemId, generalProblem)) +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala new file mode 100644 index 0000000..c08bfe1 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala @@ -0,0 +1,196 @@ +package com.automatatutor.snippet + +import java.util.Calendar +import java.util.Date +import scala.Array.canBuildFrom +import scala.Array.fallbackCanBuildFrom +import scala.xml.NodeSeq +import scala.xml.NodeSeq.seqToNodeSeq +import scala.xml.Text +import scala.xml.XML +import com.automatatutor.lib.GraderConnection +import com.automatatutor.model.Problem +import com.automatatutor.model.CYKProblem +import com.automatatutor.model.CYKSolutionAttempt +import com.automatatutor.model.SolutionAttempt +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.http.SHtml +import net.liftweb.http.SHtml.ElemAttr.pairToBasic +import net.liftweb.http.Templates +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds +import net.liftweb.http.js.JsCmds._ +import net.liftweb.http.js.JsCmds.JsHideId +import net.liftweb.http.js.JsCmds.JsShowId +import net.liftweb.http.js.JsCmds.SetHtml +import net.liftweb.http.js.JsCmds.cmdToString +import net.liftweb.http.js.JsCmds.jsExpToJsCmd +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import net.liftweb.util.Helpers.strToSuperArrowAssoc +import net.liftweb.http.js.JE.Call +import net.liftweb.common.Empty + +object CYKProblemSnippet extends ProblemSnippet { + + override def renderCreate( createUnspecificProb : (String, String) => Problem, + returnFunc : () => Nothing ) : NodeSeq = { + + def create(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val word = (formValuesXml \ "wordfield").head.text + val shortDescription = (formValuesXml \ "shortdescfield").head.text + + val parsingErrors = GraderConnection.getCNFParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val unspecificProblem = createUnspecificProb(shortDescription, shortDescription) + + val specificProblem : CYKProblem = CYKProblem.create + specificProblem.problemId(unspecificProblem).grammar(grammar).word(word) + specificProblem.save + + return JsCmds.RedirectTo("/problems/index") + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + + } + val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val wordField = SHtml.text("", value => {}, "id" -> "wordfield") + val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val wordFieldValXmlJs : String = "' + document.getElementById('wordfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + "'"), create(_)) + + val submit : JsCmd = hideSubmitButton & ajaxCall + + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("cyk-problem", "create")) openOr Text("Could not find template /cyk-problem/create") + Helpers.bind("createform", template, + "grammarfield" -> grammarField, + "wordfield" -> wordField, + "shortdescription" -> shortDescriptionField, + "submit" -> submitButton) + } + + override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc) + + private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = { + + val cykProblem = CYKProblem.findByGeneralProblem(problem) + + var shortDescription : String = problem.getShortDescription + var grammar : String = cykProblem.getGrammar + var word : String = cykProblem.getWord + + def edit(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val word = (formValuesXml \ "wordfield").head.text + val shortDescription = (formValuesXml \ "shortdescfield").head.text + + val parsingErrors = GraderConnection.getCNFParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val specificProblem : CYKProblem = CYKProblem.create + + problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save() + cykProblem.grammar(grammar).word(word).save() + returnFunc() + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + } + + val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val wordField = SHtml.text(word, word=_, "id" -> "wordfield") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val wordFieldValXmlJs : String = "' + document.getElementById('wordfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + "'"), edit(_)) + + val submit : JsCmd = hideSubmitButton & ajaxCall + + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("cyk-problem", "edit")) openOr Text("Could not find template /cyk-problem/edit") + Helpers.bind("editform", template, + "grammarfield" -> grammarField, + "wordfield" -> wordField, + "shortdescription" -> shortDescriptionField, + "submit" -> submitButton) + } + + override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt], + recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int, + bestGrade: () => Int) : NodeSeq = { + val specificProblem = CYKProblem.findByGeneralProblem(generalProblem) + + def grade(cyk_table : String) : JsCmd = { + if(remainingAttempts() <= 0) { + return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + ".")) + } + val attemptTime = Calendar.getInstance.getTime() + + val gradeAndFeedback = GraderConnection.getCYKFeedback(specificProblem.grammar.is, specificProblem.word.is, cyk_table, maxGrade.toInt) + + val numericalGrade = gradeAndFeedback._1 + val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) + + // Only save the specific attempt if we saved the general attempt + if(generalAttempt != null) { + CYKSolutionAttempt.create.solutionAttemptId(generalAttempt).attempt(cyk_table).save + } + + val setNumericalGrade : JsCmd = SetHtml("grade", Text(gradeAndFeedback._1.toString + "/" + maxGrade.toString)) + val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2) + val showFeedback : JsCmd = JsShowId("feedbackdisplay") + + return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton") + } + + val problemDescription = generalProblem.getLongDescription + val grammarText = { specificProblem.getGrammar.replaceAll("->", " -> ").replaceAll("=>", " -> ").replaceAll("\\|", " \\| ").replaceAll("\\s{2,}", " ").split("\\s(?=\\S+\\s*->)").map {Text(_) ++
} reduceLeft (_ ++ _) } + val wordText = Text("" + specificProblem.word) + + val word = specificProblem.getWord + val n = word.length() + val cyk = new Array[ Array[(Int, Int)] ] (n); + for(i <- 0 to n - 1) { + cyk(i) = new Array[(Int, Int)] (i+1) + for(j <- 0 to i) { + cyk(i)(j) = (j+1, n + j - i) + } + } + val cykTable = {cyk.map(row => {row.map(col => )})} { word.map(c => ) }
{ SHtml.text("", value => {}, "class" -> "cyk", "start" -> col._1.toString(), "end" -> col._2.toString(), "size" -> "12") }{ Text("(" + col._1.toString() + "," + col._2.toString() + ")") }
{"'" + c.toString + "'"}
+ + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("buildCYKTableXML()"), grade(_)) + val submitButton : NodeSeq = + val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) + + val template : NodeSeq = Templates(List("cyk-problem", "solve")) openOr Text("Could not find template /cyk-problem/solve") + Helpers.bind("solveform", template, + "problemdescription" -> problemDescription, + "grammartext" -> grammarText, + "wordtext" -> wordText, + "cyktable" -> cykTable, + "submitbutton" -> submitButton, + "returnlink" -> returnLink) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + + } +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/snippet/Courses.scala b/src/main/scala/com/automatatutor/snippet/Courses.scala index 43659ab..a91ee30 100644 --- a/src/main/scala/com/automatatutor/snippet/Courses.scala +++ b/src/main/scala/com/automatatutor/snippet/Courses.scala @@ -87,6 +87,7 @@ class Courses { def displaySupervisedCourses(courses : Seq[Course]) : NodeSeq = { return TableHelper.renderTableWithHeader(courses, ("Course Name", (course : Course) => Text(course.getName)), + ("Password", (course : Course) => Text(course.getPassword)), ("Contact", (course : Course) => new CourseRenderer(course).renderContactLink), ("", (course : Course) => new CourseRenderer(course).renderManageLink), ("", (course : Course) => new CourseRenderer(course).renderDeleteLink)) @@ -315,7 +316,7 @@ class Courses { } } if (startDate == null) { - S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } ) + S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } ) } else { val endDate : Date = try { @@ -328,7 +329,7 @@ class Courses { } if (endDate == null) { - S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } ) + S.redirectTo("/courses/poseproblemset", () => { CourseReqVar(course); ProblemSetReqVar(problemSet) } ) } else { val posedProblemSet = PosedProblemSet.create.setStartDate(startDate).setEndDate(endDate).setProblemSet(problemSet).setUseRandomOrder(inRandomOrder) @@ -404,8 +405,8 @@ class Courses { def renderSolveLink ( problem : PosedProblem ) : NodeSeq = { if(problem.isOpen(user, posedProblemSet)) { return SHtml.link("/courses/solveproblem", - () => { CourseReqVar(course); - PosedProblemSetReqVar(posedProblemSet); + () => { CourseReqVar(course); + PosedProblemSetReqVar(posedProblemSet); PosedProblemReqVar(problem) }, Text("solve")) } else { @@ -461,7 +462,7 @@ class Courses { val problem : Problem = posedProblem.getProblem val snippet = problem.getProblemType.getProblemSnippet return snippet.renderSolve(posedProblem.getProblem, posedProblem.getMaxGrade, Empty, - (grade, date) => SolutionAttempt, () => S.redirectTo("/courses/index"), () => 1, () => 0) + (grade, date) => SolutionAttempt, () => S.redirectTo("/practicesets/index"), () => 1, () => 0) } def renderenrollmentform ( xhtml : NodeSeq ) : NodeSeq = { diff --git a/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala new file mode 100644 index 0000000..e608a91 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala @@ -0,0 +1,183 @@ +package com.automatatutor.snippet + +import java.util.Calendar +import java.util.Date +import scala.Array.canBuildFrom +import scala.Array.fallbackCanBuildFrom +import scala.xml.NodeSeq +import scala.xml.NodeSeq.seqToNodeSeq +import scala.xml.Text +import scala.xml.XML +import com.automatatutor.lib.GraderConnection +import com.automatatutor.model.Problem +import com.automatatutor.model.DescriptionToGrammarProblem +import com.automatatutor.model.DescriptionToGrammarSolutionAttempt +import com.automatatutor.model.SolutionAttempt +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.http.SHtml +import net.liftweb.http.SHtml.ElemAttr.pairToBasic +import net.liftweb.http.Templates +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds +import net.liftweb.http.js.JsCmds._ +import net.liftweb.http.js.JsCmds.JsHideId +import net.liftweb.http.js.JsCmds.JsShowId +import net.liftweb.http.js.JsCmds.SetHtml +import net.liftweb.http.js.JsCmds.cmdToString +import net.liftweb.http.js.JsCmds.jsExpToJsCmd +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import net.liftweb.util.Helpers.strToSuperArrowAssoc +import net.liftweb.http.js.JE.Call +import net.liftweb.common.Empty + +object DescriptionToGrammarSnippet extends ProblemSnippet { + + override def renderCreate( createUnspecificProb : (String, String) => Problem, + returnFunc : () => Nothing ) : NodeSeq = { + + def create(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val shortDescription = (formValuesXml \ "shortdescfield").head.text + val longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + + val specificProblem : DescriptionToGrammarProblem = DescriptionToGrammarProblem.create + specificProblem.problemId(unspecificProblem).grammar(grammar) + specificProblem.save + + return JsCmds.RedirectTo("/problems/index") + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + + } + val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield") + val longDescriptionField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "longdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), create(_)) + val submit : JsCmd = hideSubmitButton & ajaxCall + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("description-to-grammar-problem", "create")) openOr Text("Could not find template /description-to-grammar-problem/create") + Helpers.bind("createform", template, + "grammarfield" -> grammarField, + "shortdescription" -> shortDescriptionField, + "longdescription" -> longDescriptionField, + "submit" -> submitButton) + } + + override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc) + + private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = { + + val descriptionToGrammarProblem = DescriptionToGrammarProblem.findByGeneralProblem(problem) + + var shortDescription : String = problem.getShortDescription + var longDescription : String = problem.getLongDescription + var grammar : String = descriptionToGrammarProblem.getGrammar + + def edit(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val shortDescription = (formValuesXml \ "shortdescfield").head.text + val longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val specificProblem : DescriptionToGrammarProblem = DescriptionToGrammarProblem.create + + problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() + descriptionToGrammarProblem.grammar(grammar).save() + returnFunc() + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + } + + val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield") + val longDescriptionField = SHtml.textarea(longDescription, longDescription=_, "cols" -> "80", "rows" -> "1", "id" -> "longdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), edit(_)) + + val submit : JsCmd = hideSubmitButton & ajaxCall + + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("description-to-grammar-problem", "edit")) openOr Text("Could not find template /description-to-grammar-problem/edit") + Helpers.bind("editform", template, + "grammarfield" -> grammarField, + "shortdescription" -> shortDescriptionField, + "longdescription" -> longDescriptionField, + "submit" -> submitButton) + } + + override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt], + recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int, + bestGrade: () => Int) : NodeSeq = { + val specificProblem = DescriptionToGrammarProblem.findByGeneralProblem(generalProblem) + + def grade(attemptGrammar : String) : JsCmd = { + + if(remainingAttempts() <= 0) { + return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + ".")) + } + val attemptTime = Calendar.getInstance.getTime() + + val gradeAndFeedback = GraderConnection.getDescriptionToGrammarFeedback(specificProblem.grammar.is, attemptGrammar, maxGrade.toInt) + + var numericalGrade = gradeAndFeedback._1 + val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) + + // Only save the specific attempt if we saved the general attempt and grammar was parseable + if(generalAttempt != null && numericalGrade >= 0) { + DescriptionToGrammarSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptGrammar(attemptGrammar).save + } + + if (numericalGrade < 0) numericalGrade = 0; //parse error => no pints + + val setNumericalGrade : JsCmd = SetHtml("grade", Text(numericalGrade.toString + "/" + maxGrade.toString)) + val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2) + val showFeedback : JsCmd = JsShowId("feedbackdisplay") + + return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton") + } + + val problemDescription = generalProblem.getLongDescription + val grammarField = SHtml.textarea("" , value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("document.getElementById('grammarfield').value"), grade(_)) + val submitButton : NodeSeq = + val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) + + val template : NodeSeq = Templates(List("description-to-grammar-problem", "solve")) openOr Text("Could not find template /description-to-grammar-problem/solve") + Helpers.bind("solveform", template, + "problemdescription" -> problemDescription, + "grammarfield" -> grammarField, + "submitbutton" -> submitButton, + "returnlink" -> returnLink) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + + } +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala new file mode 100644 index 0000000..624c123 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala @@ -0,0 +1,175 @@ +package com.automatatutor.snippet + +import java.util.Calendar +import java.util.Date +import scala.Array.canBuildFrom +import scala.Array.fallbackCanBuildFrom +import scala.xml.NodeSeq +import scala.xml.NodeSeq.seqToNodeSeq +import scala.xml.Text +import scala.xml.XML +import com.automatatutor.lib.GraderConnection +import com.automatatutor.model.Problem +import com.automatatutor.model.GrammarToCNFProblem +import com.automatatutor.model.GrammarToCNFSolutionAttempt +import com.automatatutor.model.SolutionAttempt +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.http.SHtml +import net.liftweb.http.SHtml.ElemAttr.pairToBasic +import net.liftweb.http.Templates +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds +import net.liftweb.http.js.JsCmds._ +import net.liftweb.http.js.JsCmds.JsHideId +import net.liftweb.http.js.JsCmds.JsShowId +import net.liftweb.http.js.JsCmds.SetHtml +import net.liftweb.http.js.JsCmds.cmdToString +import net.liftweb.http.js.JsCmds.jsExpToJsCmd +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import net.liftweb.util.Helpers.strToSuperArrowAssoc +import net.liftweb.http.js.JE.Call +import net.liftweb.common.Empty + +object GrammarToCNFSnippet extends ProblemSnippet { + + override def renderCreate( createUnspecificProb : (String, String) => Problem, + returnFunc : () => Nothing ) : NodeSeq = { + + def create(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val shortDescription = (formValuesXml \ "shortdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val unspecificProblem = createUnspecificProb(shortDescription, shortDescription) + + val specificProblem : GrammarToCNFProblem = GrammarToCNFProblem.create + specificProblem.problemId(unspecificProblem).grammar(grammar) + specificProblem.save + + return JsCmds.RedirectTo("/problems/index") + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + + } + val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + "'"), create(_)) + val submit : JsCmd = hideSubmitButton & ajaxCall + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("grammar-to-cnf-problem", "create")) openOr Text("Could not find template /grammar-to-cnf-problem/create") + Helpers.bind("createform", template, + "grammarfield" -> grammarField, + "shortdescription" -> shortDescriptionField, + "submit" -> submitButton) + } + + override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc) + + private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = { + + val grammarToCNFProblem = GrammarToCNFProblem.findByGeneralProblem(problem) + + var shortDescription : String = problem.getShortDescription + var grammar : String = grammarToCNFProblem.getGrammar + + def edit(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val shortDescription = (formValuesXml \ "shortdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val specificProblem : GrammarToCNFProblem = GrammarToCNFProblem.create + + problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save() + grammarToCNFProblem.grammar(grammar).save() + returnFunc() + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + } + + val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + "'"), edit(_)) + + val submit : JsCmd = hideSubmitButton & ajaxCall + + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("grammar-to-cnf-problem", "edit")) openOr Text("Could not find template /grammar-to-cnf-problem/edit") + Helpers.bind("editform", template, + "grammarfield" -> grammarField, + "shortdescription" -> shortDescriptionField, + "submit" -> submitButton) + } + + override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt], + recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int, + bestGrade: () => Int) : NodeSeq = { + val specificProblem = GrammarToCNFProblem.findByGeneralProblem(generalProblem) + + def grade(attemptGrammar : String) : JsCmd = { + + if(remainingAttempts() <= 0) { + return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + ".")) + } + val attemptTime = Calendar.getInstance.getTime() + + //TODO + val gradeAndFeedback = GraderConnection.getGrammarToCNFFeedback(specificProblem.grammar.is, attemptGrammar, maxGrade.toInt) + + var numericalGrade = gradeAndFeedback._1 + val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) + + // Only save the specific attempt if we saved the general attempt and grammar was parseable + if(generalAttempt != null && numericalGrade >= 0) { + GrammarToCNFSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptGrammar(attemptGrammar).save + } + + if (numericalGrade < 0) numericalGrade = 0; //parse error => no pints + + val setNumericalGrade : JsCmd = SetHtml("grade", Text(numericalGrade.toString + "/" + maxGrade.toString)) + val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2) + val showFeedback : JsCmd = JsShowId("feedbackdisplay") + + return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton") + } + + val grammarText = { specificProblem.getGrammar.replaceAll("->", " -> ").replaceAll("=>", " -> ").replaceAll("\\|", " \\| ").replaceAll("\\s{2,}", " ").split("\\s(?=\\S+\\s*->)").map {Text(_) ++
} reduceLeft (_ ++ _) } + val grammarField = SHtml.textarea("" , value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("document.getElementById('grammarfield').value"), grade(_)) + val submitButton : NodeSeq = + val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) + + val template : NodeSeq = Templates(List("grammar-to-cnf-problem", "solve")) openOr Text("Could not find template /grammar-to-cnf-problem/solve") + Helpers.bind("solveform", template, + "grammartext" -> grammarText, + "grammarfield" -> grammarField, + "submitbutton" -> submitButton, + "returnlink" -> returnLink) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + + } +} \ No newline at end of file diff --git a/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala new file mode 100644 index 0000000..23e8512 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala @@ -0,0 +1,191 @@ +package com.automatatutor.snippet + +import java.util.Calendar +import java.util.Date + +import scala.Array.canBuildFrom +import scala.Array.fallbackCanBuildFrom +import scala.xml.NodeSeq +import scala.xml.NodeSeq.seqToNodeSeq +import scala.xml.Text +import scala.xml.XML +import com.automatatutor.lib.GraderConnection +import com.automatatutor.model._ +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.http.SHtml +import net.liftweb.http.SHtml.ElemAttr.pairToBasic +import net.liftweb.http.Templates +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds +import net.liftweb.http.js.JsCmds._ +import net.liftweb.http.js.JsCmds.JsHideId +import net.liftweb.http.js.JsCmds.JsShowId +import net.liftweb.http.js.JsCmds.SetHtml +import net.liftweb.http.js.JsCmds.cmdToString +import net.liftweb.http.js.JsCmds.jsExpToJsCmd +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import net.liftweb.util.Helpers.strToSuperArrowAssoc +import net.liftweb.http.js.JE.Call +import net.liftweb.common.Empty + +object MinimizationSnippet extends ProblemSnippet { + def preprocessAutomatonXml ( input : String ) : String = input.filter(!List('\n', '\r').contains(_)).replace("\u0027", "\'") + + override def renderCreate( createUnspecificProb : (String, String) => Problem, + returnFunc : () => Nothing ) : NodeSeq = { + + var shortDescription : String = "" + var longDescription : String = "" + var automaton : String = "" + + def create() = { + val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + + val specificProblem : MinimizationProblem = MinimizationProblem.create + specificProblem.setGeneralProblem(unspecificProblem).setAutomaton(automaton) + specificProblem.save + + returnFunc() + } + + + // Remember to remove all newlines from the generated XML by using filter + val automatonField = SHtml.hidden(automatonXml => automaton = preprocessAutomatonXml(automatonXml), "", "id" -> "automatonField") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _) + val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5") + val submitButton = SHtml.submit("Create", create, "onClick" -> "document.getElementById('automatonField').value = Editor.canvas.exportAutomaton()") + + val template : NodeSeq = Templates(List("minimization", "create")) openOr Text("Could not find template /minimization/create") + Helpers.bind("createform", template, + "automaton" -> automatonField, + "shortdescription" -> shortDescriptionField, + "longdescription" -> longDescriptionField, + "submit" -> submitButton) + } + + override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc) + + private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = { + // TODO : Change DFAConstructionProblem to MinimizationProblem + val minimizationProblem = MinimizationProblem.findByGeneralProblem(problem) + + var shortDescription : String = problem.getShortDescription + var longDescription : String = problem.getLongDescription + var automaton : String = "" + + def create() = { + problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() + minimizationProblem.setAutomaton(automaton).save() + returnFunc() + } + + // Remember to remove all newlines from the generated XML by using filter + val automatonField = SHtml.hidden(automatonXml => automaton = preprocessAutomatonXml(automatonXml), "", "id" -> "automatonField") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _) + val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5") + val submitButton = SHtml.submit("Edit", create, "onClick" -> "document.getElementById('automatonField').value = Editor.canvas.exportAutomaton()") + val setupScript = + + val template : NodeSeq = Templates(List("minimization", "edit")) openOr Text("Could not find template /minimization/edit") + Helpers.bind("editform", template, + "automaton" -> automatonField, + "setupscript" -> setupScript, + "shortdescription" -> shortDescriptionField, + "longdescription" -> longDescriptionField, + "submit" -> submitButton) + } + + override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt], + recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int, + bestGrade: () => Int) : NodeSeq = { + + val minimizationProblem = MinimizationProblem.findByGeneralProblem(generalProblem) + + def grade(minimizationTableDescription : String /*, attemptDfaDescription : String*/ ) : JsCmd = { + + //Temporary: + val attemptDfaDescription = minimizationProblem.getXmlDescription.toString + + if(remainingAttempts() <= 0) { + return JsShowId("feedbackdisplay") & + SetHtml("feedbackdisplay", + Text("You do not have any attempts left for this problem. Your final grade is " + + bestGrade().toString + "/" + + maxGrade.toString + ".")) + } + + val correctDfaDescription = minimizationProblem.getXmlDescription.toString + val attemptTime = Calendar.getInstance.getTime() + //TODO: Implement getMinimizationFeedback() + val graderResponse = GraderConnection.getMinimizationFeedback(correctDfaDescription, minimizationTableDescription, attemptDfaDescription, maxGrade.toInt) + + val numericalGrade = graderResponse._1 + val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) + + // Only save the specific attempt if we saved the general attempt + if(generalAttempt != null) { + //TODO: Implement MinimizationSolutionAttempt + MinimizationSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptAutomaton(attemptDfaDescription).save + } + + val setNumericalGrade : JsCmd = SetHtml("grade", Text(graderResponse._1.toString + "/" + maxGrade.toString)) + val setFeedback : JsCmd = SetHtml("feedback", graderResponse._2) + val showFeedback : JsCmd = JsShowId("feedbackdisplay") + + return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton") + } + + val problemAlphabet = minimizationProblem.getAlphabet + val automaton = XML.loadString(minimizationProblem.getAutomaton) + val n = ((automaton \ "stateSet") \ "_").length - 1 + val cyk = new Array[ Array[(Int, Int)] ] (n); + for(i <- 0 to n - 1) { + cyk(i) = new Array[(Int, Int)] (i+1) + for(j <- 0 to i) { + cyk(i)(j) = (j, i+1) + } + } + val minimizationTable : NodeSeq = {cyk.map(row => + {row.map(col => + )} + )}
+ { + SHtml.text("", value => {}, "class" -> "cyk", "stateCount" -> (n + 1).toString(), "start" -> col._1.toString(), "end" -> col._2.toString(), "size" -> "5") + }{ + Text("(" + col._1.toString() + "," + col._2.toString() + ")") } + +
+ + val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]" + val setupScript : NodeSeq = + + + val problemAlphabetNodeSeq = Text("{" + problemAlphabet.mkString(",") + "}") + val problemDescriptionNodeSeq = Text(generalProblem.getLongDescription) + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("buildCYKTableXML()"), grade(_)) + val submitButton : NodeSeq = + val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) + + val template : NodeSeq = Templates(List("minimization", "solve")) openOr Text("Template /minimization/solve not found") + return SHtml.ajaxForm(Helpers.bind("dfaeditform", template, + "minimizationtable" -> minimizationTable, + "setupscript" -> setupScript, + "alphabettext" -> problemAlphabetNodeSeq, + "problemdescription" -> problemDescriptionNodeSeq, + "submitbutton" -> submitButton, + "returnlink" -> returnLink)) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + MinimizationProblem.deleteByGeneralProblem(generalProblem) + } +} diff --git a/src/main/scala/com/automatatutor/snippet/MinimizationTable.scala b/src/main/scala/com/automatatutor/snippet/MinimizationTable.scala new file mode 100644 index 0000000..dd50bc5 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/MinimizationTable.scala @@ -0,0 +1,69 @@ +package com.automatatutor.snippet + +import java.text.DateFormat +import java.util.Calendar +import java.util.Date + +import scala.Array.canBuildFrom +import scala.xml.NodeSeq +import scala.xml.Text + +import com.automatatutor.lib.DownloadHelper +import com.automatatutor.lib.TableHelper +import com.automatatutor.model.Attendance +import com.automatatutor.model.Course +import com.automatatutor.model.PosedProblem +import com.automatatutor.model.PosedProblemSet +import com.automatatutor.model.Problem +import com.automatatutor.model.ProblemSet +import com.automatatutor.model.SolutionAttempt +import com.automatatutor.model.Supervision +import com.automatatutor.model.User +import com.automatatutor.renderer.CourseRenderer + +import net.liftweb.common.Empty +import net.liftweb.common.Full +import net.liftweb.http.RequestVar +import net.liftweb.http.S +import net.liftweb.http.SHtml +import net.liftweb.http.SHtml.ElemAttr.pairToBasic +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.mapper.By +import net.liftweb.util.AnyVar.whatVarIs +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers.bind +import net.liftweb.util.Helpers.strToSuperArrowAssoc +import net.liftweb.util.SecurityHelpers + +/** + * Created by Jan on 08.08.2017. + */ +class MinimizationTable { + + def showall(ignored : NodeSeq) : NodeSeq = { + + val attendedCourses = User.currentUser.map(_.getAttendedCourses) openOr List() + val attendedCoursesNodeSeq = if (!attendedCourses.isEmpty) { +

Attended Courses

++ NodeSeq.Empty + } else { +

Där är inte sa manga kurser!

++ NodeSeq.Empty + } + + val supervisedCourses = User.currentUser.map(_.getSupervisedCourses) openOr List() + val supervisedCoursesNodeSeq = if (!supervisedCourses.isEmpty) { +

Supervised Courses

++ NodeSeq.Empty + } else { + NodeSeq.Empty + } + + val currentUserIsInstructor = User.currentUser.map(_.hasInstructorRole) openOr false + val createCourseLink = if(currentUserIsInstructor) { + SHtml.link("/courses/create", () => (), Text("Create new Course")) + } else { + NodeSeq.Empty + } + + return attendedCoursesNodeSeq ++ supervisedCoursesNodeSeq ++ createCourseLink + } + +} diff --git a/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala b/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala index 2c24d7d..bff3dd4 100644 --- a/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/NFAToDFAProblemSnippet.scala @@ -151,9 +151,10 @@ object NFAToDFAProblemSnippet extends ProblemSnippet { val setupScript : NodeSeq = diff --git a/src/main/scala/com/automatatutor/snippet/ProblemSets.scala b/src/main/scala/com/automatatutor/snippet/ProblemSets.scala index 6760c40..b3b233e 100644 --- a/src/main/scala/com/automatatutor/snippet/ProblemSets.scala +++ b/src/main/scala/com/automatatutor/snippet/ProblemSets.scala @@ -17,10 +17,11 @@ import com.automatatutor.lib.Binding import com.automatatutor.lib.Renderer import com.automatatutor.lib.Binder import com.automatatutor.lib.DataRenderer +import scala.collection.mutable.Set; object ProblemSetToEdit extends RequestVar[ProblemSet](null) -object ProblemToPose extends RequestVar[Problem](null) +object ProblemsToPose extends RequestVar[Set[Problem]](Set()) class Problemsets { def renderindex ( template : NodeSeq ) : NodeSeq = { @@ -87,18 +88,29 @@ class Problemsets { def renderaddproblem ( xhtml : NodeSeq ) : NodeSeq = { val problemSetToEdit = ProblemSetToEdit.is + val problemsToPose = ProblemsToPose.is val currentUser = User.currentUser openOrThrowException "Lift prevents non-logged in users from getting here" val problems : Seq[Problem] = Problem.findAllByCreator(currentUser) - return TableHelper.renderTableWithHeader(problems, + def checkAndSubmit() { + if (problemsToPose.size > 0) S.redirectTo("/problemsets/poseproblem", () => {ProblemSetToEdit(problemSetToEdit); ProblemsToPose(problemsToPose) } ) + else S.error("No problem selected"); + } + + var addProblemTable = TableHelper.renderTableWithHeader(problems, ("Short Description", (problem : Problem) => Text(problem.getShortDescription)), ("Problem Type", (problem : Problem) => Text(problem.getTypeName)), - ("", (problem : Problem) => SHtml.link("/problemsets/poseproblem", () => { ProblemSetToEdit(problemSetToEdit); ProblemToPose(problem) }, Text("Pose this problem")))) + ("Selection", (problem : Problem) => SHtml.checkbox(false, (selected) => {if (selected) problemsToPose += problem} ) ) + ) + + var addSelectedProblemsSubmitButton = SHtml.submit("Pose selected problems", checkAndSubmit ) + + return addProblemTable ++ addSelectedProblemsSubmitButton } def renderposeproblem ( xhtml : NodeSeq ) : NodeSeq = { val problemSetToEdit = ProblemSetToEdit.is - val problemToPose = ProblemToPose.is + val problemsToPose = ProblemsToPose.is var attempts = "" var maxGrade = "" @@ -106,11 +118,15 @@ class Problemsets { def poseProblem = { def redirectToSelf( exception : Exception ) = { S.error(exception.getMessage()) - S.redirectTo("/problemsets/poseproblem", () => { ProblemSetToEdit(problemSetToEdit); ProblemToPose(problemToPose) } ) + S.redirectTo("/problemsets/poseproblem", () => { ProblemSetToEdit(problemSetToEdit) } ) } val numAttempts = try { attempts.toInt } catch { case e : Exception => redirectToSelf(e) } val numMaxGrade = try { maxGrade.toInt } catch { case e : Exception => redirectToSelf(e) } - problemSetToEdit.appendProblem( problemToPose, numAttempts, numMaxGrade ) + problemsToPose.foreach( + (problem) => { + problemSetToEdit.appendProblem( problem, numAttempts, numMaxGrade ) + } + ) S.redirectTo("/problemsets/edit", () => ProblemSetToEdit(problemSetToEdit)) } diff --git a/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala new file mode 100644 index 0000000..0a8b3c8 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala @@ -0,0 +1,202 @@ +package com.automatatutor.snippet + +import java.util.Calendar +import java.util.Date + +import scala.Array.canBuildFrom +import scala.Array.fallbackCanBuildFrom +import scala.xml.NodeSeq +import scala.xml.NodeSeq.seqToNodeSeq +import scala.xml.Text +import scala.xml.XML +import com.automatatutor.lib.GraderConnection +import com.automatatutor.model._ +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.http.SHtml +import net.liftweb.http.SHtml.ElemAttr.pairToBasic +import net.liftweb.http.Templates +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds +import net.liftweb.http.js.JsCmds._ +import net.liftweb.http.js.JsCmds.JsHideId +import net.liftweb.http.js.JsCmds.JsShowId +import net.liftweb.http.js.JsCmds.SetHtml +import net.liftweb.http.js.JsCmds.cmdToString +import net.liftweb.http.js.JsCmds.jsExpToJsCmd +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import net.liftweb.util.Helpers.strToSuperArrowAssoc +import net.liftweb.http.js.JE.Call +import net.liftweb.common.Empty + +/** + * Created by Jan on 21.05.2017. + */ +object ProductConstructionSnippet extends ProblemSnippet { + def preprocessAutomatonXml ( input : String ) : String = input.filter(!List('\n', '\r').contains(_)).replace("\u0027", "\'") + + override def renderCreate( createUnspecificProb : (String, String) => Problem, + returnFunc : () => Nothing ) : NodeSeq = { + + var shortDescription : String = "" + var longDescription : String = "" + var booleanOperation : String = "" + var automaton1 : String = "" + var automaton2 : String = "" + + def create() = { + val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + + val specificProblem : ProductConstructionProblem = ProductConstructionProblem.create + specificProblem.setGeneralProblem(unspecificProblem).setAutomaton1(automaton1) + specificProblem.setGeneralProblem(unspecificProblem).setAutomaton2(automaton2) + specificProblem.setGeneralProblem(unspecificProblem).setBooleanOperation(booleanOperation) + specificProblem.save + + returnFunc() + } + + // Remember to remove all newlines from the generated XML by using filter + val automaton1Field = SHtml.hidden(automatonXml => automaton1 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton1Field") + val automaton2Field = SHtml.hidden(automatonXml => automaton2 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton2Field") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _) + val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5") + val booleanOperationField = SHtml.text(booleanOperation, booleanOperation = _) + //TODO: (I) Export both automata ; (II) Export boolean Operation ; (III) Export variable amount of automata + val submitButton = SHtml.submit("Create", create, "onClick" -> + ("document.getElementById('automaton1Field').value = Editor.canvasDfa1.exportAutomaton();" + + "document.getElementById('automaton2Field').value = Editor.canvasDfa2.exportAutomaton()")) + + val template : NodeSeq = Templates(List("product-construction", "create")) openOr Text("Could not find template /product-construction/create") + Helpers.bind("createform", template, + "automaton1" -> automaton1Field, + "automaton2" -> automaton2Field, + "boolop" -> booleanOperationField, + "shortdescription" -> shortDescriptionField, + "longdescription" -> longDescriptionField, + "submit" -> submitButton) + } + + override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc) + + private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = { + // TODO : Change DFAConstructionProblem to ProductConstructionProblem + val productConstructionProblem = ProductConstructionProblem.findByGeneralProblem(problem) + + var shortDescription : String = problem.getShortDescription + var longDescription : String = problem.getLongDescription + var booleanOperation : String = productConstructionProblem.getBooleanOperation.toString() + var automaton1 : String = "" + var automaton2 : String = "" + + def create() = { + problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() + productConstructionProblem.setBooleanOperation(booleanOperation).save() + productConstructionProblem.setAutomaton1(automaton1).save() + productConstructionProblem.setAutomaton2(automaton2).save() + returnFunc() + } + + // Remember to remove all newlines from the generated XML by using filter + val automaton1Field = SHtml.hidden(automatonXml => automaton1 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton1Field") + val automaton2Field = SHtml.hidden(automatonXml => automaton2 = preprocessAutomatonXml(automatonXml), "", "id" -> "automaton2Field") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription = _) + val longDescriptionField = SHtml.textarea(longDescription, longDescription = _, "cols" -> "80", "rows" -> "5") + val booleanOperationField = SHtml.text(booleanOperation, booleanOperation = _) + val submitButton = SHtml.submit("Edit", create, "onClick" -> + ("document.getElementById('automaton1Field').value = Editor.canvasDfa1.exportAutomaton();" + + "document.getElementById('automaton2Field').value = Editor.canvasDfa2.exportAutomaton()")) + + val automataList = productConstructionProblem.getAutomataList + val setupScript = + + + val template : NodeSeq = Templates(List("product-construction", "edit")) openOr Text("Could not find template /product-construction/edit") + Helpers.bind("editform", template, + "automaton1" -> automaton1Field, + "automaton2" -> automaton2Field, + "setupscript" -> setupScript, + "boolop" -> booleanOperationField, + "shortdescription" -> shortDescriptionField, + "longdescription" -> longDescriptionField, + "submit" -> submitButton) + } + + override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt], + recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int, + bestGrade: () => Int) : NodeSeq = { + + val productConstructionProblem = ProductConstructionProblem.findByGeneralProblem(generalProblem) + + def grade( attemptDfaDescription : String ) : JsCmd = { + if(remainingAttempts() <= 0) { + return JsShowId("feedbackdisplay") & + SetHtml("feedbackdisplay", + Text("You do not have any attempts left for this problem. Your final grade is " + + bestGrade().toString + "/" + + maxGrade.toString + ".")) + } + + val attemptDfaXml = XML.loadString(attemptDfaDescription) + //TODO - Different Automata + val correctDfaDescription1 = productConstructionProblem.getXmlDescription1.toString + val correctDfaDescription2 = productConstructionProblem.getXmlDescription2.toString + val booleanOperation = productConstructionProblem.getBooleanOperation.toString() + val attemptTime = Calendar.getInstance.getTime() + val x = List(correctDfaDescription1, correctDfaDescription2) + val graderResponse = GraderConnection.getProductConstructionFeedback(x, attemptDfaDescription, booleanOperation, maxGrade.toInt) + + val numericalGrade = graderResponse._1 + val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) + + // Only save the specific attempt if we saved the general attempt + if(generalAttempt != null) { + ProductConstructionSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptAutomaton(attemptDfaDescription).save + } + + val setNumericalGrade : JsCmd = SetHtml("grade", Text(graderResponse._1.toString + "/" + maxGrade.toString)) + val setFeedback : JsCmd = SetHtml("feedback", graderResponse._2) + val showFeedback : JsCmd = JsShowId("feedbackdisplay") + + return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton") + } + + val problemAlphabet = productConstructionProblem.getAlphabet + val automataList = productConstructionProblem.getAutomataList + + val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]" + val setupScript : NodeSeq = + + + val problemAlphabetNodeSeq = Text("{" + problemAlphabet.mkString(",") + "}") + val problemDescriptionNodeSeq = Text(generalProblem.getLongDescription) + val booleanOperationNodeSeq = Text("{" + productConstructionProblem.getBooleanOperation.toString() + "}") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("Editor.canvasDfaSol.exportAutomaton()"), grade(_)) + val submitButton : NodeSeq = + val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) + + val template : NodeSeq = Templates(List("product-construction", "solve")) openOr Text("Template /product-construction/solve not found") + return SHtml.ajaxForm(Helpers.bind("dfaeditform", template, + "setupscript" -> setupScript, + "alphabettext" -> problemAlphabetNodeSeq, + "boolop" -> booleanOperationNodeSeq, + "submitbutton" -> submitButton, + "returnlink" -> returnLink)) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + ProductConstructionProblem.deleteByGeneralProblem(generalProblem) + } +} diff --git a/src/main/scala/com/automatatutor/snippet/Users.scala b/src/main/scala/com/automatatutor/snippet/Users.scala index 193f6ac..6727603 100644 --- a/src/main/scala/com/automatatutor/snippet/Users.scala +++ b/src/main/scala/com/automatatutor/snippet/Users.scala @@ -7,6 +7,10 @@ import com.automatatutor.model.Attendance import com.automatatutor.model.Course import com.automatatutor.model.DFAConstructionProblem import com.automatatutor.model.DFAConstructionSolutionAttempt +import com.automatatutor.model.ProductConstructionProblem +import com.automatatutor.model.ProductConstructionSolutionAttempt +import com.automatatutor.model.MinimizationProblem +import com.automatatutor.model.MinimizationSolutionAttempt import com.automatatutor.model.NFAConstructionProblem import com.automatatutor.model.NFAConstructionSolutionAttempt import com.automatatutor.model.NFAToDFAProblem @@ -115,9 +119,10 @@ class Users extends PaginatorSnippet[User] { def resetlink(ignored : NodeSeq) : NodeSeq = { def resetDatabase() = { List(Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType, - DFAConstructionProblem, NFAConstructionProblem, + DFAConstructionProblem, ProductConstructionProblem, MinimizationProblem, NFAConstructionProblem, NFAToDFAProblem, NFAToDFASolutionAttempt, - ProblemSet, SolutionAttempt, Supervision, DFAConstructionSolutionAttempt, NFAConstructionSolutionAttempt).map(_.bulkDelete_!!()) + ProblemSet, SolutionAttempt, Supervision, DFAConstructionSolutionAttempt, ProductConstructionSolutionAttempt, + MinimizationSolutionAttempt, NFAConstructionSolutionAttempt).map(_.bulkDelete_!!()) } val resetLink = SHtml.link("/users/index", () => resetDatabase, Text("Reset Database")) diff --git a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala new file mode 100644 index 0000000..0f91e15 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala @@ -0,0 +1,232 @@ +package com.automatatutor.snippet + +import java.util.Calendar +import java.util.Date +import scala.Array.canBuildFrom +import scala.Array.fallbackCanBuildFrom +import scala.xml.NodeSeq +import scala.xml.NodeSeq.seqToNodeSeq +import scala.xml.Text +import scala.xml.XML +import com.automatatutor.lib.GraderConnection +import com.automatatutor.model.Problem +import com.automatatutor.model.WordsInGrammarProblem +import com.automatatutor.model.WordsInGrammarSolutionAttempt +import com.automatatutor.model.SolutionAttempt +import net.liftweb.common.Box +import net.liftweb.common.Full +import net.liftweb.http.SHtml +import net.liftweb.http.SHtml.ElemAttr.pairToBasic +import net.liftweb.http.Templates +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.http.js.JsCmd +import net.liftweb.http.js.JsCmds +import net.liftweb.http.js.JsCmds._ +import net.liftweb.http.js.JsCmds.JsHideId +import net.liftweb.http.js.JsCmds.JsShowId +import net.liftweb.http.js.JsCmds.SetHtml +import net.liftweb.http.js.JsCmds.cmdToString +import net.liftweb.http.js.JsCmds.jsExpToJsCmd +import net.liftweb.util.Helpers +import net.liftweb.util.Helpers._ +import net.liftweb.util.Helpers.strToSuperArrowAssoc +import net.liftweb.http.js.JE.Call +import net.liftweb.common.Empty + +object WordsInGrammarSnippet extends ProblemSnippet { + + override def renderCreate( createUnspecificProb : (String, String) => Problem, + returnFunc : () => Nothing ) : NodeSeq = { + + def create(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val inNeeded = (formValuesXml \ "inneededfield").head.text.toInt + val outNeeded = (formValuesXml \ "outneededfield").head.text.toInt + val shortDescription = (formValuesXml \ "shortdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val unspecificProblem = createUnspecificProb(shortDescription, shortDescription) + + val specificProblem : WordsInGrammarProblem = WordsInGrammarProblem.create + specificProblem.problemId(unspecificProblem).grammar(grammar).inNeeded(inNeeded).outNeeded(outNeeded) + specificProblem.save + + return JsCmds.RedirectTo("/problems/index") + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + + } + val grammarField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val inNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Empty , value => {}, "id" -> "inneededfield") + val outNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Empty , value => {}, "id" -> "outneededfield") + val shortDescriptionField = SHtml.text("", value => {}, "id" -> "shortdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val inNeededFieldValXmlJs : String = "' + document.getElementById('inneededfield').value + '" + val outNeededFieldValXmlJs : String = "' + document.getElementById('outneededfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + "'"), create(_)) + + //val checkGrammarAndSubmit : JsCmd = JsIf(Call("multipleAlphabetChecks",Call("parseAlphabetByFieldName", "terminalsfield"),Call("parseAlphabetByFieldName", "nonterminalsfield")), hideSubmitButton & ajaxCall) + val submit : JsCmd = hideSubmitButton & ajaxCall + + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("words-in-grammar-problem", "create")) openOr Text("Could not find template /words-in-grammar-problem/create") + Helpers.bind("createform", template, + "grammarfield" -> grammarField, + "inneededfield" -> inNeededField, + "outneededfield" -> outNeededField, + "shortdescription" -> shortDescriptionField, + "submit" -> submitButton) + } + + override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc) + + private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = { + + val wordsInGrammarProblem = WordsInGrammarProblem.findByGeneralProblem(problem) + + var shortDescription : String = problem.getShortDescription + var grammar : String = wordsInGrammarProblem.getGrammar + var inNeeded : Int = wordsInGrammarProblem.getInNeeded + var outNeeded : Int = wordsInGrammarProblem.getOutNeeded + + def edit(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + val grammar = (formValuesXml \ "grammarfield").head.text + val inNeeded = (formValuesXml \ "inneededfield").head.text.toInt + val outNeeded = (formValuesXml \ "outneededfield").head.text.toInt + val shortDescription = (formValuesXml \ "shortdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val specificProblem : WordsInGrammarProblem = WordsInGrammarProblem.create + + problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save() + wordsInGrammarProblem.grammar(grammar).inNeeded(inNeeded).outNeeded(outNeeded).save() + returnFunc() + } else { + return JsCmds.JsShowId("submitbutton") & JsCmds.JsShowId("feedbackdisplay") & JsCmds.SetHtml("parsingerror", Text(parsingErrors.mkString("
"))) + } + } + + val grammarField = SHtml.textarea(grammar, grammar=_, "cols" -> "80", "rows" -> "5", "id" -> "grammarfield") + val inNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Full("" + inNeeded) , value => {}, "id" -> "inneededfield") + val outNeededField = SHtml.select( Array(("1","1"), ("2","2"), ("3","3"), ("4","4"), ("5","5")), Full("" + outNeeded) , value => {}, "id" -> "outneededfield") + val shortDescriptionField = SHtml.text(shortDescription, shortDescription=_, "id" -> "shortdescfield") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val grammarFieldValXmlJs : String = "' + document.getElementById('grammarfield').value + '" + val inNeededFieldValXmlJs : String = "' + document.getElementById('inneededfield').value + '" + val outNeededFieldValXmlJs : String = "' + document.getElementById('outneededfield').value + '" + val shortdescFieldValXmlJs : String = "' + document.getElementById('shortdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + "'"), edit(_)) + + val submit : JsCmd = hideSubmitButton & ajaxCall + + val submitButton : NodeSeq = + + val template : NodeSeq = Templates(List("words-in-grammar-problem", "edit")) openOr Text("Could not find template /words-in-grammar-problem/edit") + Helpers.bind("editform", template, + "grammarfield" -> grammarField, + "inneededfield" -> inNeededField, + "outneededfield" -> outNeededField, + "shortdescription" -> shortDescriptionField, + "submit" -> submitButton) + } + + override def renderSolve(generalProblem : Problem, maxGrade : Long, lastAttempt : Box[SolutionAttempt], + recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int, + bestGrade: () => Int) : NodeSeq = { + val specificProblem = WordsInGrammarProblem.findByGeneralProblem(generalProblem) + + def grade(formValues : String) : JsCmd = { + val formValuesXml = XML.loadString(formValues) + + val wordsIn = new Array[String](specificProblem.getInNeeded); + for(i <- 0 to specificProblem.getInNeeded - 1) { + wordsIn(i) = (formValuesXml \ "ins" \ ("in" + i)).head.text.replaceAll("\\s", "") + } + val wordsOut = new Array[String](specificProblem.getOutNeeded); + for(i <- 0 to specificProblem.getOutNeeded - 1) { + wordsOut(i) = (formValuesXml \ "outs" \ ("out" + i)).head.text.replaceAll("\\s", "") + } + + if(remainingAttempts() <= 0) { + return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", Text("You do not have any attempts left for this problem. Your final grade is " + bestGrade().toString + "/" + maxGrade.toString + ".")) + } + val attemptTime = Calendar.getInstance.getTime() + + val gradeAndFeedback = GraderConnection.getWordsInGrammarFeedback(specificProblem.grammar.is, wordsIn, wordsOut, maxGrade.toInt) + + val numericalGrade = gradeAndFeedback._1 + val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) + + // Only save the specific attempt if we saved the general attempt + if(generalAttempt != null) { + WordsInGrammarSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptWordsIn((formValuesXml \ "ins").toString()).attemptWordsOut((formValuesXml \ "outs").toString()).save + } + + val setNumericalGrade : JsCmd = SetHtml("grade", Text(gradeAndFeedback._1.toString + "/" + maxGrade.toString)) + val setFeedback : JsCmd = SetHtml("feedback", gradeAndFeedback._2) + val showFeedback : JsCmd = JsShowId("feedbackdisplay") + + return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton") + } + + val problemDescription = generalProblem.getLongDescription + val grammarText = { specificProblem.getGrammar.replaceAll("->", " -> ").replaceAll("=>", " -> ").replaceAll("\\|", " \\| ").replaceAll("\\s{2,}", " ").split("\\s(?=\\S+\\s*->)").map {Text(_) ++
} reduceLeft (_ ++ _) } + var inNeededText = Text(specificProblem.inNeeded + " words") + if (specificProblem.inNeeded == 1) inNeededText = Text(specificProblem.inNeeded + " word") + var outNeededText = Text(specificProblem.outNeeded + " words") + if (specificProblem.outNeeded == 1) outNeededText = Text(specificProblem.outNeeded + " word") + val wordsInFields = new Array[NodeSeq](specificProblem.getInNeeded) + for(i <- 0 to specificProblem.getInNeeded - 1) { + wordsInFields(i) = SHtml.text("", value => {}, "id" -> ("wordinfield" + i.toString), "maxlength" -> "75") + } + val wordsInFieldNodeSeq = + val wordsOutFields = new Array[NodeSeq](specificProblem.getOutNeeded) + for(i <- 0 to specificProblem.getOutNeeded - 1) { + wordsOutFields(i) = SHtml.text("", value => {}, "id" -> ("wordoutfield" + i.toString), "maxlength" -> "75") + } + val wordsOutFieldNodeSeq = + + val insValXmlJs : StringBuilder = new StringBuilder("") + for(i <- 0 to specificProblem.getInNeeded - 1) { + insValXmlJs.append("' + sanitizeInputForXML('wordinfield" + i.toString + "') + '") + } + insValXmlJs.append("") + val outsValXmlJs : StringBuilder = new StringBuilder("") + for(i <- 0 to specificProblem.getOutNeeded - 1) { + outsValXmlJs.append("' + sanitizeInputForXML('wordoutfield" + i.toString + "') + '") + } + outsValXmlJs.append("") + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + insValXmlJs + outsValXmlJs + "'"), grade(_)) + val submitButton : NodeSeq = + val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) + + val template : NodeSeq = Templates(List("words-in-grammar-problem", "solve")) openOr Text("Could not find template /words-in-grammar-problem/solve") + Helpers.bind("solveform", template, + "problemdescription" -> problemDescription, + "grammartext" -> grammarText, + "wordsin" -> wordsInFieldNodeSeq, + "wordsout" -> wordsOutFieldNodeSeq, + "inneededtext" -> inNeededText, + "outneededtext" -> outNeededText, + "submitbutton" -> submitButton, + "returnlink" -> returnLink) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + + } +} \ No newline at end of file diff --git a/src/main/webapp/courses/index.html b/src/main/webapp/courses/index.html index 414ef86..057e2f4 100644 --- a/src/main/webapp/courses/index.html +++ b/src/main/webapp/courses/index.html @@ -6,6 +6,7 @@ +

Enroll in course

diff --git a/src/main/webapp/cyk-problem/create.html b/src/main/webapp/cyk-problem/create.html new file mode 100644 index 0000000..dc9a08b --- /dev/null +++ b/src/main/webapp/cyk-problem/create.html @@ -0,0 +1,43 @@ +
+

Create a new CYK problem

+ +

Grammar Syntax

+ + +

Problem Definition

+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/cyk-problem/edit.html b/src/main/webapp/cyk-problem/edit.html new file mode 100644 index 0000000..36d1394 --- /dev/null +++ b/src/main/webapp/cyk-problem/edit.html @@ -0,0 +1,43 @@ +
+

Edit a CYK problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem Definition

+ + +
Grammar:
Word:
Short Description:
+ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/cyk-problem/solve.html b/src/main/webapp/cyk-problem/solve.html new file mode 100644 index 0000000..6e94fb4 --- /dev/null +++ b/src/main/webapp/cyk-problem/solve.html @@ -0,0 +1,59 @@ +
+ +

Solve a CYK problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem

+ + For the grammar with the productions:
+

+ + Perform the CYK algorithm! + +

+ +
+
+ +

+ + +
+ + + + +
diff --git a/src/main/webapp/description-to-grammar-problem/create.html b/src/main/webapp/description-to-grammar-problem/create.html new file mode 100644 index 0000000..545e71f --- /dev/null +++ b/src/main/webapp/description-to-grammar-problem/create.html @@ -0,0 +1,43 @@ +
+

Create a new Description to Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem Definition

+ + +
Grammar:
Word:
Short Description:
+ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/description-to-grammar-problem/edit.html b/src/main/webapp/description-to-grammar-problem/edit.html new file mode 100644 index 0000000..47ce597 --- /dev/null +++ b/src/main/webapp/description-to-grammar-problem/edit.html @@ -0,0 +1,46 @@ +
+ + + +

Edit a Description to Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem Definition

+ + +
Grammar:
Short Description:
Long Description
(Will be display after "Find a grammar that recognizes the following language: ..."):
+ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/description-to-grammar-problem/solve.html b/src/main/webapp/description-to-grammar-problem/solve.html new file mode 100644 index 0000000..a73b9ed --- /dev/null +++ b/src/main/webapp/description-to-grammar-problem/solve.html @@ -0,0 +1,34 @@ +
+

Solve Description to Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem

+ + Find a grammar that recognizes the following language:
+


+ +
+ + +
+ + + + +
diff --git a/src/main/webapp/description-to-regex-problem/create.html b/src/main/webapp/description-to-regex-problem/create.html index c6b06c9..2bcf5fa 100644 --- a/src/main/webapp/description-to-regex-problem/create.html +++ b/src/main/webapp/description-to-regex-problem/create.html @@ -20,7 +20,8 @@

Problem Definition

- + + diff --git a/src/main/webapp/grammar-to-cnf-problem/create.html b/src/main/webapp/grammar-to-cnf-problem/create.html new file mode 100644 index 0000000..4469931 --- /dev/null +++ b/src/main/webapp/grammar-to-cnf-problem/create.html @@ -0,0 +1,39 @@ +
+

Create a new Grammar to CNF (Comsky Normal Form) problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem Definition

+ + +
Grammar:
Short Description:
Long Description
(Will be display after "Find a grammar that recognizes the following language: ..."):
Regular Expression:
Short Description:
+ + + + + + + + + + + + + + + + diff --git a/src/main/webapp/grammar-to-cnf-problem/edit.html b/src/main/webapp/grammar-to-cnf-problem/edit.html new file mode 100644 index 0000000..e3731bf --- /dev/null +++ b/src/main/webapp/grammar-to-cnf-problem/edit.html @@ -0,0 +1,39 @@ +
+

Edit a Grammar to CNF (Comsky Normal Form) problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem Definition

+ + +
Grammar:
Short Description:
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/grammar-to-cnf-problem/solve.html b/src/main/webapp/grammar-to-cnf-problem/solve.html new file mode 100644 index 0000000..331e276 --- /dev/null +++ b/src/main/webapp/grammar-to-cnf-problem/solve.html @@ -0,0 +1,39 @@ +
+

Solve Grammar to CNF (Comsky Normal Form) problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem

+ + For the grammar G with the productions:
+

+ + Give a CNF G' such that L(G) \ "" = L(G')
+ (remember: CNF allows only productions with the form "X -> Y Z" or "X -> a") + +

+ +
+
+ +
+ + + + +
diff --git a/src/main/webapp/javascript/alphabetUtil.js b/src/main/webapp/javascript/alphabetUtil.js index 9604b60..3acf08f 100644 --- a/src/main/webapp/javascript/alphabetUtil.js +++ b/src/main/webapp/javascript/alphabetUtil.js @@ -39,4 +39,12 @@ function alphabetChecks(stringArray){ tmpArray.push(elem); } return true; +} + +function multipleAlphabetChecks(){ + if (arguments.length == 0) return true; + var concatenated = arguments[0]; + var i; + for(i = 1; i < arguments.length; i++) concatenated.concat(arguments[i]); + return alphabetChecks(concatenated); } \ No newline at end of file diff --git a/src/main/webapp/javascript/grammarUtil.js b/src/main/webapp/javascript/grammarUtil.js new file mode 100644 index 0000000..3daa55e --- /dev/null +++ b/src/main/webapp/javascript/grammarUtil.js @@ -0,0 +1,15 @@ +function formateGrammar(grammarString) { + var res = grammarString.replace(/->/g, " -> "); + res = res.replace(/=>/g, " -> "); + res = res.replace(/\|/g, " | "); + res = res.replace(/\s{2,}/g, " ") + res = res.replace(/\s(?=\S+\s*->)/, "\n"); + return res; +} + +function sanitizeInputForXML(id) { + var input = document.getElementById(id); + if (input === null) return ""; + input.value = input.value.replace(/[<>&]/g, ""); + return input.value; +} \ No newline at end of file diff --git a/src/main/webapp/javascript/includeDFANFA.js b/src/main/webapp/javascript/includeDFANFA.js index 950635c..5781bed 100644 --- a/src/main/webapp/javascript/includeDFANFA.js +++ b/src/main/webapp/javascript/includeDFANFA.js @@ -12,7 +12,7 @@ function initCanvas() { Editor.canvasNfa = new $.SvgCanvas("#svgcanvasnfa", Editor.curConfigNfa, 'nondetaut'); } if(!Editor.canvasDfa) { - Editor.canvasDfa = new $.SvgCanvas("#svgcanvasdfa", Editor.curConfigDfa, 'detaut'); + Editor.canvasDfa = new $.SvgCanvas("#svgcanvasdfa", Editor.curConfigDfa, 'powaut'); } } diff --git a/src/main/webapp/javascript/includeMinimizationCreateEdit.js b/src/main/webapp/javascript/includeMinimizationCreateEdit.js new file mode 100644 index 0000000..8c0b1d0 --- /dev/null +++ b/src/main/webapp/javascript/includeMinimizationCreateEdit.js @@ -0,0 +1,17 @@ +var Editor = { + curConfigDfa: { + dimensions: [740,480] + } +}; + +function initCanvas() { + if(!Editor.canvas) { + Editor.canvas = new $.SvgCanvas("#svgcanvasdfa", Editor.curConfigDfa, 'detaut'); + } +} + +$(document).ready(function() { + initCanvas(); +}); + + diff --git a/src/main/webapp/javascript/includeMinimizationSolve.js b/src/main/webapp/javascript/includeMinimizationSolve.js new file mode 100644 index 0000000..ac13106 --- /dev/null +++ b/src/main/webapp/javascript/includeMinimizationSolve.js @@ -0,0 +1,23 @@ +var Editor = { + curConfigDfaIn: { + dimensions: [740,480] + }, + curConfigDfaSol: { + dimensions: [740,480] + } +}; + +function initCanvas() { + if(!Editor.canvasDfaIn) { + Editor.canvasDfaIn = new $.SvgCanvas("#svgcanvasdfain", Editor.curConfigDfaIn, 'detaut'); + } + if(!Editor.canvasDfaSol) { + Editor.canvasDfaSol = new $.SvgCanvas("#svgcanvasdfasol", Editor.curConfigDfaSol, 'powaut'); + } +} + +$(document).ready(function() { + initCanvas(); +}); + + diff --git a/src/main/webapp/javascript/includeProductConstruction.js b/src/main/webapp/javascript/includeProductConstruction.js new file mode 100644 index 0000000..4bf689a --- /dev/null +++ b/src/main/webapp/javascript/includeProductConstruction.js @@ -0,0 +1,40 @@ +var Editor = { + curConfig: { + dimensions: [740,480] + }, + canvasArray: new Array() +}; + +function initCanvas() { + if(Editor.canvas) + return; + + createCanvas(1); +} + +function createCanvas(no) { + var oldLength = Editor.canvasArray.length; + if(oldLength < no){ + for(var i = oldLength; i < no; i++){ + Editor.canvasArray.push(new $.SvgCanvas("#svgcanvasdfa", Editor.curConfig, 'detaut')); + } + } else { + Editor.canvasArray.length = no; + } + + Editor.canvas = Editor.canvasArray[0]; +} + +function setNumberOfCanvas(no){ + createCanvas(no) +} + +//TODO +function setActualCanvas(no) { + Editor.canvas = Editor.canvasArray[no]; +} + +$(document).ready(function() { + initCanvas(); +}); + diff --git a/src/main/webapp/javascript/includeProductConstructionCreateEdit.js b/src/main/webapp/javascript/includeProductConstructionCreateEdit.js new file mode 100644 index 0000000..4d2ad51 --- /dev/null +++ b/src/main/webapp/javascript/includeProductConstructionCreateEdit.js @@ -0,0 +1,23 @@ +var Editor = { + curConfigDfa1: { + dimensions: [740,480] + }, + curConfigDfa2: { + dimensions: [740,480] + } +}; + +function initCanvas() { + if(!Editor.canvasDfa1) { + Editor.canvasDfa1 = new $.SvgCanvas("#svgcanvascreatedfa1", Editor.curConfigDfa1, 'detaut'); + } + if(!Editor.canvasDfa2) { + Editor.canvasDfa2 = new $.SvgCanvas("#svgcanvascreatedfa2", Editor.curConfigDfa2, 'detaut'); + } +} + +$(document).ready(function() { + initCanvas(); +}); + + diff --git a/src/main/webapp/javascript/includeProductConstructionSolve.js b/src/main/webapp/javascript/includeProductConstructionSolve.js new file mode 100644 index 0000000..b43faa9 --- /dev/null +++ b/src/main/webapp/javascript/includeProductConstructionSolve.js @@ -0,0 +1,29 @@ +var Editor = { + curConfigDfa1: { + dimensions: [360,240] + }, + curConfigDfa2: { + dimensions: [360,240] + }, + curConfigDfaSol: { + dimensions: [740,480] + } +}; + +function initCanvas() { + if(!Editor.canvasDfa1) { + Editor.canvasDfa1 = new $.SvgCanvas("#svgcanvasdfa1", Editor.curConfigDfa1, 'detaut'); + } + if(!Editor.canvasDfa2) { + Editor.canvasDfa2 = new $.SvgCanvas("#svgcanvasdfa2", Editor.curConfigDfa2, 'detaut'); + } + if(!Editor.canvasDfaSol) { + Editor.canvasDfaSol = new $.SvgCanvas("#svgcanvasdfasol", Editor.curConfigDfaSol, 'prodaut'); + } +} + +$(document).ready(function() { + initCanvas(); +}); + + diff --git a/src/main/webapp/javascript/interface.js b/src/main/webapp/javascript/interface.js index 342c79b..466ab2f 100644 --- a/src/main/webapp/javascript/interface.js +++ b/src/main/webapp/javascript/interface.js @@ -45,6 +45,35 @@ $.SvgCanvas = function(container, config, style) { acceptanceMarker: 'css' }, + //Product automaton: States are labelled as tuples of states of the original automata + 'prodaut': { + node: { + label: 'tuple' + }, + transition: { + labeled: true, + deterministic: true + }, + hasInitialNode: true, + twoPlayers: false, + acceptanceMarker: 'css' + }, + + //Powerset automaton: State are labelled as sets of states of the original automaton + 'powaut': { + node: { + label: 'set', + radius: 25 + }, + transition: { + labeled: true, + deterministic: true + }, + hasInitialNode: true, + twoPlayers: false, + acceptanceMarker: 'css' + }, + 'nondetaut': { node: { label: 'id' @@ -111,7 +140,7 @@ $.SvgCanvas = function(container, config, style) { $.extend(true, config, globalConfig, styleConfig[style]) - function useHoverMenu() { return config.transition.labeled === false || config.transition.deterministic === false } + function useHoverMenu() { return config.transition.labeled === false || config.transition.deterministic === false || config.node.label === 'tuple' || config.node.label === 'set' } /// Returns true iff the hover menu around node d should be displayed function isHoverMenuVisible(d) { return (d.menu_visible && !newLink && !draggingLink && !draggingNode && showMenu)} @@ -196,6 +225,128 @@ $.SvgCanvas = function(container, config, style) { } } + //TODO: + function populateHoverMenusWithNumbersBothSides(menus) { + var symbolsToDistributeLeft = numberOfNodesOfAutomaton1 + var symbolsToDistributeRight = numberOfNodesOfAutomaton2 + + function calculateXCoordinateLeft(index) { + var angle = 3*Math.PI/2 - (Math.PI/2 - symbolsToDistributeLeft*config.hoverMenu.step/2) - (index + .5) * config.hoverMenu.step; + return polarToPlanar(config.node.radius + 10, angle).x; + } + function calculateYCoordinateLeft(index) { + var angle = 3*Math.PI/2 - (Math.PI/2 - symbolsToDistributeLeft*config.hoverMenu.step/2) - (index + .5) * config.hoverMenu.step; + return polarToPlanar(config.node.radius + 10, angle).y + 5; + } + + function calculateXCoordinateRight(index) { + var angle = 3*Math.PI/2 + (Math.PI/2 - symbolsToDistributeRight*config.hoverMenu.step/2) + (index + .5) * config.hoverMenu.step; + return polarToPlanar(config.node.radius + 10, angle).x; + } + function calculateYCoordinateRight(index) { + var angle = 3*Math.PI/2 + (Math.PI/2 - symbolsToDistributeRight*config.hoverMenu.step/2) + (index + .5) * config.hoverMenu.step; + return polarToPlanar(config.node.radius + 10, angle).y + 5; + } + + function onLabelMouseover(node) { + node.menu_visible = true; + showMenu = true; + restart(); + } + + function onLabelMousedown(node) { + node.menu_visible = false; + showMenu = false; + newLink = true; + mousedown_node = node; + + if(this.getAttribute('pos') === 'left') + mousedown_node.left = this.textContent; + else + mousedown_node.right = this.textContent; + + restart(); + } + + function onLabelMouseout(nodeData) { showMenu = false } + + for(var i = 0; i < symbolsToDistributeLeft; i++){ + menus.append('svg:text') + .attr('class', 'hoverMenu visible') + .classed('visible', isHoverMenuVisible) + .text(i) + .attr('pos', 'left') + .attr('x', calculateXCoordinateLeft(i)) + .attr('y', calculateYCoordinateLeft(i)) + .on('mouseover', onLabelMouseover) + .on('mousedown', onLabelMousedown) + .on('mouseout', onLabelMouseout); + } + + for(var i = 0; i < symbolsToDistributeRight; i++){ + menus.append('svg:text') + .attr('class', 'hoverMenu visible') + .classed('visible', isHoverMenuVisible) + .text(i) + .attr('pos', 'right') + .attr('x', calculateXCoordinateRight(i)) + .attr('y', calculateYCoordinateRight(i)) + .on('mouseover', onLabelMouseover) + .on('mousedown', onLabelMousedown) + .on('mouseout', onLabelMouseout); + } + } + + function populateHoverMenusWithNumbers(menus) { + + var symbolsToDistribute = numberOfNodesOfAutomaton1 + + function calculateXCoordinate(index) { + var angle = 3*Math.PI/2 - (symbolsToDistribute-1)/2 * config.hoverMenu.step + index * config.hoverMenu.step; + return polarToPlanar(config.node.radius + 10, angle).x; + } + function calculateYCoordinate(index) { + var angle = 3*Math.PI/2 - (symbolsToDistribute-1)/2 * config.hoverMenu.step + index * config.hoverMenu.step; + return polarToPlanar(config.node.radius + 10, angle).y + 5; + } + + function onLabelMouseover(node){ + node.menu_visible = true + showMenu = true + restart(); + } + function onLabelMousedown(node){ + node.menu_visible = false; + showMenu = false; + newLink = true; + mousedown_node = node; + + if(mousedown_node.states.length === 0){ + for(var i = 0; i < symbolsToDistribute; i++){ + mousedown_node.states.push(false); + } + } + + mousedown_node.states[this.getAttribute('index')] = !mousedown_node.states[this.getAttribute('index')]; + } + function onLabelMouseout(nodeData){ + showMenu = false + } + + for(var i = 0; i < symbolsToDistribute; i++){ + menus.append('svg:text') + .attr('class', 'hoverMenu visible') + .classed('visible', isHoverMenuVisible) + .text(i) + .attr('index', i) + .attr('x', calculateXCoordinate(i)) + .attr('y', calculateYCoordinate(i)) + .on('mouseover', onLabelMouseover) + .on('mousedown', onLabelMousedown) + .on('mouseout', onLabelMouseout); + } + } + function populateHoverMenusWithUnlabeled(menus) { menus.append('svg:path') .attr('class', 'link hoverMenu') @@ -300,6 +451,8 @@ $.SvgCanvas = function(container, config, style) { var width = config.dimensions[0]; var height = config.dimensions[1]; + + var started = false; var locked = false; @@ -316,6 +469,10 @@ $.SvgCanvas = function(container, config, style) { var solveMode = false + //for product construction + var numberOfNodesOfAutomaton1 = 4, + numberOfNodesOfAutomaton2 = 4; + // init D3 force layout var force; @@ -785,6 +942,21 @@ $.SvgCanvas = function(container, config, style) { case 'id': circle.selectAll('text').text(function (d) { return d.id }) break + case 'tuple': + circle.selectAll('text').text(function (d) {return d.left + ',' + d.right }) + break + case 'set': + circle.selectAll('text').text( + function (d) { + var s = '' + for(var i = 0; i < d.states.length; i++){ + if(d.states[i]){ + s += i + ','; + } + } + return s.slice(0, -1) + }) + break case 'priority': circle.selectAll('text').text(function (d) { return d.priority }) break @@ -952,6 +1124,7 @@ $.SvgCanvas = function(container, config, style) { restart(); } + //rim of regular node g.append('svg:circle') .attr('class', 'node') .attr('id', 'main') @@ -1018,6 +1191,21 @@ $.SvgCanvas = function(container, config, style) { case 'id': newLabels.text(function (d) { return d.id }); break + case 'tuple': + newLabels.text(function (d) { return d.left + ',' + d.right }); + break + case 'set': + circle.selectAll('text').text( + function (d) { + var s = '' + for(var i = 0; i < d.states.length; i++){ + if(d.states[i]){ + s += i + ','; + } + } + return s.slice(0, -1) + }) + break case 'priority': newLabels.text(function (d) { return d.priority }); break @@ -1057,8 +1245,12 @@ $.SvgCanvas = function(container, config, style) { hoverMenu.selectAll('text').classed('visible', isHoverMenuVisible); // Hide the prototype transition if the hover menu is not active hoverMenu.selectAll('path').classed('visible', isHoverMenuVisible); - - if(config.transition.labeled) { + + if(config.node.label === 'tuple'){ + populateHoverMenusWithNumbersBothSides(menus) + } else if(config.node.label === 'set'){ + populateHoverMenusWithNumbers(menus) + } else if(config.transition.labeled) { populateHoverMenusWithAlphabet(menus) } else { populateHoverMenusWithUnlabeled(menus) @@ -1755,6 +1947,9 @@ $.SvgCanvas = function(container, config, style) { // Just push the info about the new node to nodes. Canvas will be updated at the next restart() var node = { id: idNum, + left: 0, //for 'prodaut' + right: 0, //for 'prodaut' + states: [], //for 'powaut' initial: false, accepting: false, reflexiveNum: reflNum, @@ -2013,8 +2208,8 @@ $.SvgCanvas = function(container, config, style) { var stateTags = xmlDoc.getElementsByTagName("stateSet")[0].getElementsByTagName("state"); for (i = 0; i < stateTags.length; i++) { var currState = stateTags[i]; - var posX = parseFloat(currState.getElementsByTagName("posX")[0].firstChild.nodeValue); - var posY = parseFloat(currState.getElementsByTagName("posY")[0].firstChild.nodeValue); + var posX = parseFloat(currState.getElementsByTagName("posX")[0].firstChild.nodeValue) * width / 1000; + var posY = parseFloat(currState.getElementsByTagName("posY")[0].firstChild.nodeValue) * height / 1000; var nodeId = parseInt(currState.getElementsByTagName("label")[0].firstChild.nodeValue); addNode(posX, posY); nodes[nodes.length - 1].id = nodeId; @@ -2137,7 +2332,35 @@ $.SvgCanvas = function(container, config, style) { if(config.node.label === 'priority') { states += "priority='" + nodes[i].priority + "' " } - states += ">" + Math.round(parseFloat(nodes[i].x)) + "" + Math.round(parseFloat(nodes[i].y)) + "\n"; + states += ">" + nodes[i].id + ""; + if(config.node.label === 'tuple') { + states += "" + } + else if(config.node.label === 'set') { + states += "" + } + else { + states += "" + } + + states += "" + Math.round(parseFloat(nodes[i].x) * 1000 / width) + "" + Math.round(parseFloat(nodes[i].y) * 1000 / height) + "\n"; } states = states + " \n"; @@ -2220,6 +2443,25 @@ $.SvgCanvas = function(container, config, style) { this.initialize(); } + /** + * Sets the number of states of the automata to construct product from + * + */ + this.setNumberOfStates = function(xml1, xml2) { + + var xml1Doc = Utils.text2xml(xml1); + var xml2Doc = Utils.text2xml(xml2); + var stateTags1 = xml1Doc.getElementsByTagName("stateSet")[0].getElementsByTagName("state"); + var stateTags2 = xml2Doc.getElementsByTagName("stateSet")[0].getElementsByTagName("state"); + + numberOfNodesOfAutomaton1 = stateTags1.length + numberOfNodesOfAutomaton2 = stateTags2.length + + this.initialize(); + this.restart(); + + } + /** * Sets whether or not to use epsilon transitions * diff --git a/src/main/webapp/minimization/applet-solve.html b/src/main/webapp/minimization/applet-solve.html new file mode 100644 index 0000000..93e4fdf --- /dev/null +++ b/src/main/webapp/minimization/applet-solve.html @@ -0,0 +1 @@ + diff --git a/src/main/webapp/minimization/applet.html b/src/main/webapp/minimization/applet.html new file mode 100644 index 0000000..f133ee1 --- /dev/null +++ b/src/main/webapp/minimization/applet.html @@ -0,0 +1,33 @@ +
+ + + + + + + + + + + + + + +
+ +
+
+
+ +
+ + + +
diff --git a/src/main/webapp/minimization/create.html b/src/main/webapp/minimization/create.html new file mode 100644 index 0000000..6181c1d --- /dev/null +++ b/src/main/webapp/minimization/create.html @@ -0,0 +1,57 @@ +
+ + + + + +

Create a new Minimization problem

+ +
+ Note that resetting the alphabet will also reset the whole automaton
+ Alphabet (separated by spaces): + + + NOT: Number of automata to construct product from: +
+ + + + + + + +
Grammar:
Short Description:
+ + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/minimization/edit.html b/src/main/webapp/minimization/edit.html new file mode 100644 index 0000000..2a30a48 --- /dev/null +++ b/src/main/webapp/minimization/edit.html @@ -0,0 +1,52 @@ +
+ + + + + +

Edit minimization problem

+ +
+ Note that resetting the alphabet will also reset the whole automaton
+ Alphabet (separated by spaces): +
+ + This makes no sense whatsoever: +
+ + + + + + +
Short Description:
Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+ + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/minimization/solve.html b/src/main/webapp/minimization/solve.html new file mode 100644 index 0000000..04e148e --- /dev/null +++ b/src/main/webapp/minimization/solve.html @@ -0,0 +1,91 @@ +
+ + +

Solve minimization problem

+
+ Construct an automaton that recognizes the following language of strings over the alphabet + :
+ +
+
+ + + +
+ + + + + + + + + + + + + + +
+ +
+
+
+ +
+ + +
+
+ +
+ + + +
+ +
+ + + + + + + + + + +
diff --git a/src/main/webapp/problemsets/addproblem.html b/src/main/webapp/problemsets/addproblem.html index 617421e..65e7e98 100644 --- a/src/main/webapp/problemsets/addproblem.html +++ b/src/main/webapp/problemsets/addproblem.html @@ -2,9 +2,10 @@ Home -
-

Problems created by you

- -
+
+

Problems created by you

+ + +
diff --git a/src/main/webapp/product-construction/applet-create-edit.html b/src/main/webapp/product-construction/applet-create-edit.html new file mode 100644 index 0000000..7c6ce2a --- /dev/null +++ b/src/main/webapp/product-construction/applet-create-edit.html @@ -0,0 +1,40 @@ +
+ + + + + + + + + + + + + + +
+ +
+
+
+
+ +
+ + + + +
+ diff --git a/src/main/webapp/product-construction/applet-solve-product.html b/src/main/webapp/product-construction/applet-solve-product.html new file mode 100644 index 0000000..b54d880 --- /dev/null +++ b/src/main/webapp/product-construction/applet-solve-product.html @@ -0,0 +1,46 @@ +
+ + + + + + + + + + + + + + +
+ +
+
+ + +
+
+
+ +
+ + + + +
+ diff --git a/src/main/webapp/product-construction/applet.html b/src/main/webapp/product-construction/applet.html new file mode 100644 index 0000000..85f2d3d --- /dev/null +++ b/src/main/webapp/product-construction/applet.html @@ -0,0 +1,33 @@ +
+ + + + + + + + + + + + + + +
+ +
+
+
+ +
+ + + +
diff --git a/src/main/webapp/product-construction/create.html b/src/main/webapp/product-construction/create.html new file mode 100644 index 0000000..6375754 --- /dev/null +++ b/src/main/webapp/product-construction/create.html @@ -0,0 +1,66 @@ +
+ + + + + +

Create a new Product-construction problem

+ +
+ Note that resetting the alphabet will also reset the whole automaton
+ Alphabet (separated by spaces): +
+ + Number of automata to construct product from: +
+ + + + + + + +
Short Description:
Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/product-construction/edit.html b/src/main/webapp/product-construction/edit.html new file mode 100644 index 0000000..9c9ee61 --- /dev/null +++ b/src/main/webapp/product-construction/edit.html @@ -0,0 +1,61 @@ +
+ + + + + +

Edit product-construction problem

+ +
+ Note that resetting the alphabet will also reset the whole automaton
+ Alphabet (separated by spaces): +
+ + Number of automata to construct product from: +
+ + + + + + +
Boolean Operation:
Short Description:
Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/product-construction/solve.html b/src/main/webapp/product-construction/solve.html new file mode 100644 index 0000000..fa51025 --- /dev/null +++ b/src/main/webapp/product-construction/solve.html @@ -0,0 +1,30 @@ +
+

Solve product-construction problem

+
+ Combine the given automata via product construction + :
+ using the boolean operation + +
+
+ + + +
+ + + + + + + + + + +
diff --git a/src/main/webapp/stylesheets/proto2.css b/src/main/webapp/stylesheets/proto2.css index 0aefa4e..9b5c434 100644 --- a/src/main/webapp/stylesheets/proto2.css +++ b/src/main/webapp/stylesheets/proto2.css @@ -33,6 +33,20 @@ padding: 3px 6px !important; overflow: auto; } +#automata_tutor #svgcanvasdfa1.svgcanvas { + background-color: #FFFFFF; + width: 365px; + height: 240px; + margin: 5px; +} + +#automata_tutor #svgcanvasdfa2.svgcanvas { + background-color: #FFFFFF; + width: 365px; + height: 240px; + margin: 5px; +} + #automata_tutor #svgcanvas { background-color: #FFFFFF; width: 740px; @@ -47,6 +61,48 @@ padding: 3px 6px !important; margin: 5px; } +#automata_tutor #svgcanvasdfa1 { + background-color: #FFFFFF; + width: 330px; + height: 240px; + margin: 5px; +} + +#automata_tutor #svgcanvasdfa2 { + background-color: #FFFFFF; + width: 330px; + height: 240px; + margin: 5px; +} + +#automata_tutor #svgcanvascreatedfa1 { + background-color: #FFFFFF; + width: 740px; + height: 480px; + margin: 5px; +} + +#automata_tutor #svgcanvascreatedfa2 { + background-color: #FFFFFF; + width: 740px; + height: 480px; + margin: 5px; +} + +#automata_tutor #svgcanvasdfain { + background-color: #FFFFFF; + width: 740px; + height: 480px; + margin: 5px; +} + +#automata_tutor #svgcanvasdfasol { + background-color: #FFFFFF; + width: 740px; + height: 480px; + margin: 5px; +} + #automata_tutor #svgcanvasnfa { background-color: #FFFFFF; width: 740px; diff --git a/src/main/webapp/words-in-grammar-problem/create.html b/src/main/webapp/words-in-grammar-problem/create.html new file mode 100644 index 0000000..32991d2 --- /dev/null +++ b/src/main/webapp/words-in-grammar-problem/create.html @@ -0,0 +1,47 @@ +
+

Create a new Words in Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem Definition

+ + +
Boolean Operation:
Short Description:
Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/words-in-grammar-problem/edit.html b/src/main/webapp/words-in-grammar-problem/edit.html new file mode 100644 index 0000000..a1d2ac0 --- /dev/null +++ b/src/main/webapp/words-in-grammar-problem/edit.html @@ -0,0 +1,47 @@ +
+

Edit a Words in Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem Definition

+ + +
Number of words IN grammar:
Number of words NOT IN grammar:
Grammar:
Short Description:
+ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/webapp/words-in-grammar-problem/solve.html b/src/main/webapp/words-in-grammar-problem/solve.html new file mode 100644 index 0000000..5495767 --- /dev/null +++ b/src/main/webapp/words-in-grammar-problem/solve.html @@ -0,0 +1,42 @@ +
+ + +

Solve Words in Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • split consecutive variables with a whitespace (e.g. S -> A B C)
  • +
  • first variable is start variable
  • +
  • possible terminals: abcdefghijklmnopqrstuvwxyz()[]{}
  • +
  • possible variables: 1 or more upper case characters A-Z or numbers 0-9 (!can't start with number!)
  • +
  • empty word: a blank right side (e.g. "S -> " or "S -> a | ")
  • +
+ +

Problem

+ + For the grammar with the productions:
+

+ + Give that the grammar recognizes and that the grammar doesn't recognize! + +

+ +
+ words in the grammar: + words NOT in the grammar: + + +
+ + + + +
Number of words IN grammar:
Number of words NOT IN grammar:
Grammar:
Short Description: