From 1da518e607e865277a7d05df2f18da3238073776 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Sun, 21 May 2017 18:59:27 +0200 Subject: [PATCH 01/25] UI changes (quality of life): Adding multiple Problems to ProblemSet; Showing Course Password in Overview --- .../com/automatatutor/snippet/Courses.scala | 1 + .../automatatutor/snippet/ProblemSets.scala | 28 +++++++++++++++---- src/main/webapp/problemsets/addproblem.html | 9 +++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/main/scala/com/automatatutor/snippet/Courses.scala b/src/main/scala/com/automatatutor/snippet/Courses.scala index 43659ab..fb81fcf 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)) 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/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

+ + +
From 3a7ec4d28e152e0b48633cfe7974a1e381446574 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Thu, 25 May 2017 13:56:03 +0200 Subject: [PATCH 02/25] =?UTF-8?q?Ger=C3=BCst=20f=C3=BCr=20Produktkonstrukt?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../automatatutor/lib/SOAPConnection.scala | 21 +++ .../com/automatatutor/model/Problem.scala | 11 +- .../snippet/ProductConstructionSnippet.scala | 165 ++++++++++++++++++ 3 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala index 519c33f..60bad51 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -118,6 +118,27 @@ object GraderConnection { return ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li")) } + + //Product Construction + + def getProductConstructionFeedback(correctDfaDescriptionList : List[String], attemptDfaDescription : 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), + "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")) + } // NFA diff --git a/src/main/scala/com/automatatutor/model/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala index 0571712..a68b463 100644 --- a/src/main/scala/com/automatatutor/model/Problem.scala +++ b/src/main/scala/com/automatatutor/model/Problem.scala @@ -1,11 +1,6 @@ package com.automatatutor.model -import com.automatatutor.snippet.DFAConstructionSnippet -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 com.automatatutor.snippet._ import net.liftweb.common.Empty import net.liftweb.common.Full import net.liftweb.mapper.By @@ -29,12 +24,14 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { val EnglishToRegExTypeName = "English to Regular Expression" //val PLTypeName = "Pumping Lemma Proof" val BuchiSolvingTypeName = "Buchi Game Solving" + val ProductConstructionTypeName = "Product Construction" val knownProblemTypes : Map[String, ProblemSnippet] = Map( DFAConstructionTypeName -> DFAConstructionSnippet, NFAConstructionTypeName -> NFAProblemSnippet, NFAToDFATypeName -> NFAToDFAProblemSnippet, - EnglishToRegExTypeName -> RegExConstructionSnippet + EnglishToRegExTypeName -> RegExConstructionSnippet, + ProductConstructionTypeName -> ProductConstructionSnippet ) ++ (if(Config.buchiGameSolving.enabled.get) { Map(BuchiSolvingTypeName -> BuchiGameSolving.SnippetAdapter) } else { Map[String, ProblemSnippet]() }) 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..a014d14 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala @@ -0,0 +1,165 @@ +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 automaton : String = "" + + def create() = { + val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + + val specificProblem : DFAConstructionProblem = DFAConstructionProblem.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("dfa-construction", "create")) openOr Text("Could not find template /dfa-construction/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 = { + val dfaConstructionProblem = DFAConstructionProblem.findByGeneralProblem(problem) + + var shortDescription : String = problem.getShortDescription + var longDescription : String = problem.getLongDescription + var automaton : String = "" + + def create() = { + problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() + dfaConstructionProblem.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("dfa-construction", "edit")) openOr Text("Could not find template /dfa-construction/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 dfaConstructionProblem = DFAConstructionProblem.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) + val correctDfaDescription = dfaConstructionProblem.getXmlDescription.toString + val attemptTime = Calendar.getInstance.getTime() + // TODO - Different Automata + val x = List(correctDfaDescription, correctDfaDescription) + val graderResponse = GraderConnection.getProductConstructionFeedback(x, 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) { + DFAConstructionSolutionAttempt.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 = dfaConstructionProblem.getAlphabet + + val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]" + val alphabetScript : NodeSeq = + + val problemAlphabetNodeSeq = Text("{" + problemAlphabet.mkString(",") + "}") + val problemDescriptionNodeSeq = Text(generalProblem.getLongDescription) + + val hideSubmitButton : JsCmd = JsHideId("submitbutton") + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("Editor.canvas.exportAutomaton()"), grade(_)) + val submitButton : NodeSeq = + val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) + + val template : NodeSeq = Templates(List("dfa-construction", "solve")) openOr Text("Template /dfa-construction/solve not found") + return SHtml.ajaxForm(Helpers.bind("dfaeditform", template, + "alphabetscript" -> alphabetScript, + "alphabettext" -> problemAlphabetNodeSeq, + "problemdescription" -> problemDescriptionNodeSeq, + "submitbutton" -> submitButton, + "returnlink" -> returnLink)) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + DFAConstructionProblem.deleteByGeneralProblem(generalProblem) + } +} From df4c7b2f9d95e9c3b4d3cf17eac4048c7238cb41 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Sun, 11 Jun 2017 12:29:10 +0200 Subject: [PATCH 03/25] =?UTF-8?q?Ger=C3=BCst=20f=C3=BCr=20Produktkonstrukt?= =?UTF-8?q?ion=20funktionierend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/scala/bootstrap/liftweb/Boot.scala | 1 + .../com/automatatutor/model/Problem.scala | 1 + .../model/ProductConstructionProblem.scala | 45 ++++++++++++++++ .../automatatutor/model/SolutionAttempt.scala | 15 +++++- .../snippet/ProductConstructionSnippet.scala | 27 +++++----- .../com/automatatutor/snippet/Users.scala | 6 ++- .../javascript/includeProductConstruction.js | 35 +++++++++++++ .../webapp/product-construction/applet.html | 33 ++++++++++++ .../webapp/product-construction/create.html | 49 +++++++++++++++++ .../webapp/product-construction/edit.html | 52 +++++++++++++++++++ .../webapp/product-construction/solve.html | 29 +++++++++++ 11 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala create mode 100644 src/main/webapp/javascript/includeProductConstruction.js create mode 100644 src/main/webapp/product-construction/applet.html create mode 100644 src/main/webapp/product-construction/create.html create mode 100644 src/main/webapp/product-construction/edit.html create mode 100644 src/main/webapp/product-construction/solve.html diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index d1132b2..44557c5 100644 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -40,6 +40,7 @@ class Boot { Schemifier.schemify(true, Schemifier.infoF _, User, Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType, DFAConstructionProblem, DFAConstructionSolutionAttempt, + ProductConstructionProblem, ProductConstructionSolutionAttempt, NFAConstructionProblem, NFAConstructionSolutionAttempt, NFAToDFAProblem, NFAToDFASolutionAttempt, RegExConstructionProblem, RegexConstructionSolutionAttempt, diff --git a/src/main/scala/com/automatatutor/model/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala index a68b463..e0d48a9 100644 --- a/src/main/scala/com/automatatutor/model/Problem.scala +++ b/src/main/scala/com/automatatutor/model/Problem.scala @@ -44,6 +44,7 @@ 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 NFAConstructionTypeName => NFAConstructionProblem.findByGeneralProblem(generalProblem) case NFAToDFATypeName => NFAToDFAProblem.findByGeneralProblem(generalProblem) case EnglishToRegExTypeName => RegExConstructionProblem.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..46f1eb1 --- /dev/null +++ b/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala @@ -0,0 +1,45 @@ +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 object automaton extends MappedText(this) + + def getGeneralProblem = this.problemId.obj openOrThrowException "Every ProductConstructionProblem must have a ProblemId" + override def setGeneralProblem(problem : Problem) : ProductConstructionProblem = 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(): ProductConstructionProblem = { + val retVal = new ProductConstructionProblem + retVal.problemId(this.problemId.get) + retVal.automaton(this.automaton.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..9025625 100644 --- a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala +++ b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala @@ -31,7 +31,20 @@ 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" + } +} + +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 DFA construction attempt" } } diff --git a/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala index a014d14..a404491 100644 --- a/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala @@ -47,7 +47,7 @@ object ProductConstructionSnippet extends ProblemSnippet { def create() = { val unspecificProblem = createUnspecificProb(shortDescription, longDescription) - val specificProblem : DFAConstructionProblem = DFAConstructionProblem.create + val specificProblem : ProductConstructionProblem = ProductConstructionProblem.create specificProblem.setGeneralProblem(unspecificProblem).setAutomaton(automaton) specificProblem.save @@ -61,7 +61,7 @@ object ProductConstructionSnippet extends ProblemSnippet { 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("dfa-construction", "create")) openOr Text("Could not find template /dfa-construction/create") + val template : NodeSeq = Templates(List("product-construction", "create")) openOr Text("Could not find template /product-construction/create") Helpers.bind("createform", template, "automaton" -> automatonField, "shortdescription" -> shortDescriptionField, @@ -72,7 +72,8 @@ object ProductConstructionSnippet extends ProblemSnippet { override def renderEdit : Box[(Problem, () => Nothing) => NodeSeq] = Full(renderEditFunc) private def renderEditFunc(problem : Problem, returnFunc : () => Nothing) : NodeSeq = { - val dfaConstructionProblem = DFAConstructionProblem.findByGeneralProblem(problem) + // TODO : Change DFAConstructionProblem to ProductConstructionProblem + val productConstructionProblem = ProductConstructionProblem.findByGeneralProblem(problem) var shortDescription : String = problem.getShortDescription var longDescription : String = problem.getLongDescription @@ -80,7 +81,7 @@ object ProductConstructionSnippet extends ProblemSnippet { def create() = { problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() - dfaConstructionProblem.setAutomaton(automaton).save() + productConstructionProblem.setAutomaton(automaton).save() returnFunc() } @@ -89,9 +90,9 @@ object ProductConstructionSnippet extends ProblemSnippet { 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 setupScript = - val template : NodeSeq = Templates(List("dfa-construction", "edit")) openOr Text("Could not find template /dfa-construction/edit") + val template : NodeSeq = Templates(List("product-construction", "edit")) openOr Text("Could not find template /product-construction/edit") Helpers.bind("editform", template, "automaton" -> automatonField, "setupscript" -> setupScript, @@ -104,7 +105,7 @@ object ProductConstructionSnippet extends ProblemSnippet { recordSolutionAttempt : (Int, Date) => SolutionAttempt, returnFunc : () => Unit, remainingAttempts: () => Int, bestGrade: () => Int) : NodeSeq = { - val dfaConstructionProblem = DFAConstructionProblem.findByGeneralProblem(generalProblem) + val productConstructionProblem = ProductConstructionProblem.findByGeneralProblem(generalProblem) def grade( attemptDfaDescription : String ) : JsCmd = { if(remainingAttempts() <= 0) { @@ -116,9 +117,9 @@ object ProductConstructionSnippet extends ProblemSnippet { } val attemptDfaXml = XML.loadString(attemptDfaDescription) - val correctDfaDescription = dfaConstructionProblem.getXmlDescription.toString + //TODO - Different Automata + val correctDfaDescription = productConstructionProblem.getXmlDescription.toString val attemptTime = Calendar.getInstance.getTime() - // TODO - Different Automata val x = List(correctDfaDescription, correctDfaDescription) val graderResponse = GraderConnection.getProductConstructionFeedback(x, attemptDfaDescription, maxGrade.toInt) @@ -127,7 +128,7 @@ object ProductConstructionSnippet extends ProblemSnippet { // Only save the specific attempt if we saved the general attempt if(generalAttempt != null) { - DFAConstructionSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptAutomaton(attemptDfaDescription).save + ProductConstructionSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptAutomaton(attemptDfaDescription).save } val setNumericalGrade : JsCmd = SetHtml("grade", Text(graderResponse._1.toString + "/" + maxGrade.toString)) @@ -137,7 +138,7 @@ object ProductConstructionSnippet extends ProblemSnippet { return setNumericalGrade & setFeedback & showFeedback & JsCmds.JsShowId("submitbutton") } - val problemAlphabet = dfaConstructionProblem.getAlphabet + val problemAlphabet = productConstructionProblem.getAlphabet val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]" val alphabetScript : NodeSeq = @@ -150,7 +151,7 @@ object ProductConstructionSnippet extends ProblemSnippet { val submitButton : NodeSeq = val returnLink : NodeSeq = SHtml.link("/courses/show", returnFunc, Text("Return to Course")) - val template : NodeSeq = Templates(List("dfa-construction", "solve")) openOr Text("Template /dfa-construction/solve not found") + val template : NodeSeq = Templates(List("product-construction", "solve")) openOr Text("Template /product-construction/solve not found") return SHtml.ajaxForm(Helpers.bind("dfaeditform", template, "alphabetscript" -> alphabetScript, "alphabettext" -> problemAlphabetNodeSeq, @@ -160,6 +161,6 @@ object ProductConstructionSnippet extends ProblemSnippet { } override def onDelete( generalProblem : Problem ) : Unit = { - DFAConstructionProblem.deleteByGeneralProblem(generalProblem) + 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..f34c3fb 100644 --- a/src/main/scala/com/automatatutor/snippet/Users.scala +++ b/src/main/scala/com/automatatutor/snippet/Users.scala @@ -7,6 +7,8 @@ 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.NFAConstructionProblem import com.automatatutor.model.NFAConstructionSolutionAttempt import com.automatatutor.model.NFAToDFAProblem @@ -115,9 +117,9 @@ class Users extends PaginatorSnippet[User] { def resetlink(ignored : NodeSeq) : NodeSeq = { def resetDatabase() = { List(Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType, - DFAConstructionProblem, NFAConstructionProblem, + DFAConstructionProblem, ProductConstructionProblem, NFAConstructionProblem, NFAToDFAProblem, NFAToDFASolutionAttempt, - ProblemSet, SolutionAttempt, Supervision, DFAConstructionSolutionAttempt, NFAConstructionSolutionAttempt).map(_.bulkDelete_!!()) + ProblemSet, SolutionAttempt, Supervision, DFAConstructionSolutionAttempt, ProductConstructionSolutionAttempt, NFAConstructionSolutionAttempt).map(_.bulkDelete_!!()) } val resetLink = SHtml.link("/users/index", () => resetDatabase, Text("Reset Database")) diff --git a/src/main/webapp/javascript/includeProductConstruction.js b/src/main/webapp/javascript/includeProductConstruction.js new file mode 100644 index 0000000..b5060f0 --- /dev/null +++ b/src/main/webapp/javascript/includeProductConstruction.js @@ -0,0 +1,35 @@ +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) +} + +$(document).ready(function() { + initCanvas(); +}); + 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..f6c789b --- /dev/null +++ b/src/main/webapp/product-construction/create.html @@ -0,0 +1,49 @@ +
+ + + + + +

Create a new DFA-construction problem

+ +
+ Note that resetting the alphabet will also reset the whole automaton
+ Alphabet (separated by spaces): + +
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/product-construction/edit.html b/src/main/webapp/product-construction/edit.html new file mode 100644 index 0000000..968e5a3 --- /dev/null +++ b/src/main/webapp/product-construction/edit.html @@ -0,0 +1,52 @@ +
+ + + + + +

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: +
+ + + + + + +
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..91872da --- /dev/null +++ b/src/main/webapp/product-construction/solve.html @@ -0,0 +1,29 @@ +
+

Solve product-construction problem

+
+ Construct an automaton that recognizes the following language of strings over the alphabet + :
+ +
+
+ + + +
+ + + + + + + + + + +
From c7851e92cbcb5acb7ceb7a077f373373abd976d0 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Thu, 6 Jul 2017 22:44:02 +0200 Subject: [PATCH 04/25] new exercise types ("Description to Grammar" and "Grammar To CNF") --- develop.db.trace.db | 32 +++ src/main/scala/bootstrap/liftweb/Boot.scala | 3 + .../automatatutor/lib/SOAPConnection.scala | 56 ++++ .../model/DescriptionToGrammarProblem.scala | 41 +++ .../model/GrammarToCNFProblem.scala | 41 +++ .../com/automatatutor/model/Problem.scala | 16 +- .../automatatutor/model/SolutionAttempt.scala | 34 +++ .../model/WordsInGrammarProblem.scala | 49 ++++ .../snippet/DescriptionToGrammarSnippet.scala | 183 ++++++++++++++ .../snippet/GrammarToCNFSnippet.scala | 184 ++++++++++++++ .../snippet/WordsInGrammarSnippet.scala | 239 ++++++++++++++++++ .../create.html | 42 +++ .../description-to-grammar-problem/edit.html | 45 ++++ .../description-to-grammar-problem/solve.html | 33 +++ .../description-to-regex-problem/create.html | 3 +- .../webapp/grammar-to-cnf-problem/create.html | 42 +++ .../webapp/grammar-to-cnf-problem/edit.html | 42 +++ .../webapp/grammar-to-cnf-problem/solve.html | 38 +++ src/main/webapp/javascript/alphabetUtil.js | 8 + src/main/webapp/javascript/grammarUtil.js | 8 + .../words-in-grammar-problem/create.html | 50 ++++ .../webapp/words-in-grammar-problem/edit.html | 50 ++++ .../words-in-grammar-problem/solve.html | 39 +++ 23 files changed, 1275 insertions(+), 3 deletions(-) create mode 100644 develop.db.trace.db create mode 100644 src/main/scala/com/automatatutor/model/DescriptionToGrammarProblem.scala create mode 100644 src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala create mode 100644 src/main/scala/com/automatatutor/model/WordsInGrammarProblem.scala create mode 100644 src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala create mode 100644 src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala create mode 100644 src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala create mode 100644 src/main/webapp/description-to-grammar-problem/create.html create mode 100644 src/main/webapp/description-to-grammar-problem/edit.html create mode 100644 src/main/webapp/description-to-grammar-problem/solve.html create mode 100644 src/main/webapp/grammar-to-cnf-problem/create.html create mode 100644 src/main/webapp/grammar-to-cnf-problem/edit.html create mode 100644 src/main/webapp/grammar-to-cnf-problem/solve.html create mode 100644 src/main/webapp/javascript/grammarUtil.js create mode 100644 src/main/webapp/words-in-grammar-problem/create.html create mode 100644 src/main/webapp/words-in-grammar-problem/edit.html create mode 100644 src/main/webapp/words-in-grammar-problem/solve.html diff --git a/develop.db.trace.db b/develop.db.trace.db new file mode 100644 index 0000000..114655f --- /dev/null +++ b/develop.db.trace.db @@ -0,0 +1,32 @@ +06-13 15:41:32 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] +06-13 15:41:32 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] +06-13 15:41:33 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] +06-13 15:41:33 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] +06-13 15:41:35 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] +06-13 15:41:35 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] +06-13 15:41:40 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] +06-13 15:41:40 jdbc[4]: exception +org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden +Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: +INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index d1132b2..6f9952a 100644 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -44,6 +44,9 @@ class Boot { NFAToDFAProblem, NFAToDFASolutionAttempt, RegExConstructionProblem, RegexConstructionSolutionAttempt, PumpingLemmaProblem, PumpingLemmaSolutionAttempt, + WordsInGrammarProblem, WordsInGrammarSolutionAttempt, + GrammarToCNFProblem, GrammarToCNFSolutionAttempt, + DescriptionToGrammarProblem, DescriptionToGrammarSolutionAttempt, ProblemSet, Role, SolutionAttempt, Supervision) StartupHooks.hooks map (hook => hook()) diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala index 519c33f..5a565a5 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -166,6 +166,7 @@ object GraderConnection { } def getRegexParsingErrors(potentialRegex : String, alphabet : Seq[String]) : Seq[String] = { + return List() val arguments = Map[String, Node]( "regexDesc" ->
{ potentialRegex }
, "alphabet" ->
{ alphabet.map((symbol : String) => Elem(null, "symbol", Null, TopScope, true, Text(symbol))) }
) @@ -175,6 +176,61 @@ 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 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 (0, (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")) + } + // Pumping lemma def getPLParsingErrors(languageDesc : String, constraintDesc : String, 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/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala index 0571712..595bcdd 100644 --- a/src/main/scala/com/automatatutor/model/Problem.scala +++ b/src/main/scala/com/automatatutor/model/Problem.scala @@ -6,6 +6,9 @@ import com.automatatutor.snippet.NFAToDFAProblemSnippet import com.automatatutor.snippet.ProblemSnippet import com.automatatutor.snippet.RegExConstructionSnippet import com.automatatutor.snippet.PumpingLemmaProblemSnippet +import com.automatatutor.snippet.WordsInGrammarSnippet +import com.automatatutor.snippet.DescriptionToGrammarSnippet +import com.automatatutor.snippet.GrammarToCNFSnippet import net.liftweb.common.Empty import net.liftweb.common.Full import net.liftweb.mapper.By @@ -27,14 +30,20 @@ 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 knownProblemTypes : Map[String, ProblemSnippet] = Map( DFAConstructionTypeName -> DFAConstructionSnippet, NFAConstructionTypeName -> NFAProblemSnippet, NFAToDFATypeName -> NFAToDFAProblemSnippet, - EnglishToRegExTypeName -> RegExConstructionSnippet + EnglishToRegExTypeName -> RegExConstructionSnippet, + WordsInGrammarTypeName -> WordsInGrammarSnippet, + DescriptionToGrammarTypeName -> DescriptionToGrammarSnippet, + GrammarToCNFTypeName -> GrammarToCNFSnippet ) ++ (if(Config.buchiGameSolving.enabled.get) { Map(BuchiSolvingTypeName -> BuchiGameSolving.SnippetAdapter) } else { Map[String, ProblemSnippet]() }) @@ -50,6 +59,9 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { 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) } } diff --git a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala index 0dc4f2d..f90fbff 100644 --- a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala +++ b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala @@ -68,6 +68,40 @@ object RegexConstructionSolutionAttempt extends RegexConstructionSolutionAttempt } +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 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/DescriptionToGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala new file mode 100644 index 0000000..cb8e510 --- /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 = + + println("test"); + + 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) + + val 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 + } + + 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 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..7f7ab00 --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala @@ -0,0 +1,184 @@ +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 longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + + 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 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 = + + println("test"); + + 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, + "longdescription" -> longDescriptionField, + "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 longDescription : String = problem.getLongDescription + 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 longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val specificProblem : GrammarToCNFProblem = GrammarToCNFProblem.create + + problem.setShortDescription(shortDescription).setLongDescription(longDescription).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 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("grammar-to-cnf-problem", "edit")) openOr Text("Could not find template /grammar-to-cnf-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 = 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) + + val 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 + } + + 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 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/WordsInGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala new file mode 100644 index 0000000..c551d5a --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala @@ -0,0 +1,239 @@ +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 longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + + 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 longDescriptionField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "longdescfield") + + 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), 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, + "longdescription" -> longDescriptionField, + "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 longDescription : String = problem.getLongDescription + 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 longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val specificProblem : WordsInGrammarProblem = WordsInGrammarProblem.create + + problem.setShortDescription(shortDescription).setLongDescription(longDescription).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 longDescriptionField = SHtml.textarea(longDescription, longDescription=_, "cols" -> "80", "rows" -> "1", "id" -> "longdescfield") + + 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), 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, + "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 = 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 (_ ++ _) } + val inNeededText = Text("" + specificProblem.inNeeded) + val outNeededText = Text("" + specificProblem.outNeeded) + val wordsInFields = new Array[NodeSeq](specificProblem.getInNeeded) + for(i <- 0 to specificProblem.getInNeeded - 1) { + wordsInFields(i) = SHtml.text("", value => {}, "id" -> ("wordinfield" + i.toString)) + } + val wordsInFieldNodeSeq =
    {wordsInFields.map(i =>
  • {i}
  • )}
+ val wordsOutFields = new Array[NodeSeq](specificProblem.getOutNeeded) + for(i <- 0 to specificProblem.getOutNeeded - 1) { + wordsOutFields(i) = SHtml.text("", value => {}, "id" -> ("wordoutfield" + i.toString)) + } + val wordsOutFieldNodeSeq =
    {wordsOutFields.map(i =>
  • {i}
  • )}
+ + val insValXmlJs : StringBuilder = new StringBuilder("") + for(i <- 0 to specificProblem.getInNeeded - 1) { + insValXmlJs.append("' + document.getElementById('wordinfield" + i.toString + "').value + '") + } + insValXmlJs.append("") + val outsValXmlJs : StringBuilder = new StringBuilder("") + for(i <- 0 to specificProblem.getOutNeeded - 1) { + outsValXmlJs.append("' + document.getElementById('wordoutfield" + i.toString + "').value + '") + } + 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/description-to-grammar-problem/create.html b/src/main/webapp/description-to-grammar-problem/create.html new file mode 100644 index 0000000..835cf4b --- /dev/null +++ b/src/main/webapp/description-to-grammar-problem/create.html @@ -0,0 +1,42 @@ +
+

Create a new Description to Grammar Problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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

+ + +
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/description-to-grammar-problem/edit.html b/src/main/webapp/description-to-grammar-problem/edit.html new file mode 100644 index 0000000..22fa8c4 --- /dev/null +++ b/src/main/webapp/description-to-grammar-problem/edit.html @@ -0,0 +1,45 @@ +
+ + + +

Edit a Description to Grammar Problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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..0255800 --- /dev/null +++ b/src/main/webapp/description-to-grammar-problem/solve.html @@ -0,0 +1,33 @@ +
+

Solve Description to Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
  • +
+ +

The 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..7338c8f --- /dev/null +++ b/src/main/webapp/grammar-to-cnf-problem/create.html @@ -0,0 +1,42 @@ +
+

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
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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..7dab415 --- /dev/null +++ b/src/main/webapp/grammar-to-cnf-problem/edit.html @@ -0,0 +1,42 @@ +
+

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
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 not be displayed):
+ + + + + + + + + + + + + + + + + + + + \ 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..2b2e432 --- /dev/null +++ b/src/main/webapp/grammar-to-cnf-problem/solve.html @@ -0,0 +1,38 @@ +
+

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
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
  • +
+ +

The Problem

+ + For the grammar G with the productions:
+

+ + Give a CNF G' such that L(G) \ "" = L(G')
+ (remember: CNF means only "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..bf96d7e --- /dev/null +++ b/src/main/webapp/javascript/grammarUtil.js @@ -0,0 +1,8 @@ +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; +} \ No newline at end of file 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..2d61b46 --- /dev/null +++ b/src/main/webapp/words-in-grammar-problem/create.html @@ -0,0 +1,50 @@ +
+

Create a new Words in Grammar Problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 not be displayed...):
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..784a6bc --- /dev/null +++ b/src/main/webapp/words-in-grammar-problem/edit.html @@ -0,0 +1,50 @@ +
+

Edit a Words in Grammar Problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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:
Long Description (will not be displayed):
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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..a167e6e --- /dev/null +++ b/src/main/webapp/words-in-grammar-problem/solve.html @@ -0,0 +1,39 @@ +
+

Solve Words in Grammar problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
  • +
+ +

The Problem

+ + For the grammar with the productions:
+

+ + Give words that are recognized and words that aren't recognized! + +

+ +
+ words in the grammar: + words NOT in the grammar: + + +
+ + + + +
From e0d687b6251f75c1ff589d1477c5c7cda924d25e Mon Sep 17 00:00:00 2001 From: ga63nij Date: Mon, 10 Jul 2017 21:25:34 +0200 Subject: [PATCH 05/25] new exercise type: CYK Algorithm + some minor layout changes --- src/main/scala/bootstrap/liftweb/Boot.scala | 1 + .../automatatutor/lib/SOAPConnection.scala | 23 ++ .../com/automatatutor/model/CYKProblem.scala | 45 ++++ .../com/automatatutor/model/Problem.scala | 6 +- .../automatatutor/model/SolutionAttempt.scala | 11 + .../snippet/CYKProblemSnippet.scala | 205 ++++++++++++++++++ .../snippet/WordsInGrammarSnippet.scala | 4 +- src/main/webapp/cyk-problem/create.html | 42 ++++ src/main/webapp/cyk-problem/edit.html | 42 ++++ src/main/webapp/cyk-problem/solve.html | 52 +++++ .../webapp/grammar-to-cnf-problem/create.html | 4 - .../webapp/grammar-to-cnf-problem/edit.html | 4 - src/main/webapp/javascript/grammarUtil.js | 7 + .../words-in-grammar-problem/create.html | 4 - .../webapp/words-in-grammar-problem/edit.html | 4 - .../words-in-grammar-problem/solve.html | 2 + 16 files changed, 437 insertions(+), 19 deletions(-) create mode 100644 src/main/scala/com/automatatutor/model/CYKProblem.scala create mode 100644 src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala create mode 100644 src/main/webapp/cyk-problem/create.html create mode 100644 src/main/webapp/cyk-problem/edit.html create mode 100644 src/main/webapp/cyk-problem/solve.html diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index 6f9952a..a4cbc1c 100644 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -47,6 +47,7 @@ class Boot { WordsInGrammarProblem, WordsInGrammarSolutionAttempt, GrammarToCNFProblem, GrammarToCNFSolutionAttempt, DescriptionToGrammarProblem, DescriptionToGrammarSolutionAttempt, + CYKProblem, CYKSolutionAttempt, ProblemSet, Role, SolutionAttempt, Supervision) StartupHooks.hooks map (hook => hook()) diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala index 5a565a5..ed77af3 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -187,6 +187,16 @@ object GraderConnection { 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)), @@ -231,6 +241,19 @@ object GraderConnection { 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/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala index 595bcdd..c0d0891 100644 --- a/src/main/scala/com/automatatutor/model/Problem.scala +++ b/src/main/scala/com/automatatutor/model/Problem.scala @@ -8,6 +8,7 @@ import com.automatatutor.snippet.RegExConstructionSnippet import com.automatatutor.snippet.PumpingLemmaProblemSnippet import com.automatatutor.snippet.WordsInGrammarSnippet import com.automatatutor.snippet.DescriptionToGrammarSnippet +import com.automatatutor.snippet.CYKProblemSnippet import com.automatatutor.snippet.GrammarToCNFSnippet import net.liftweb.common.Empty import net.liftweb.common.Full @@ -35,6 +36,7 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { val WordsInGrammarTypeName = "Words in Grammar" val DescriptionToGrammarTypeName = "English to Grammar" val GrammarToCNFTypeName = "Grammar to CNF" + val CYKTypeName = "CYK Algorithm" val knownProblemTypes : Map[String, ProblemSnippet] = Map( DFAConstructionTypeName -> DFAConstructionSnippet, @@ -43,7 +45,8 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { EnglishToRegExTypeName -> RegExConstructionSnippet, WordsInGrammarTypeName -> WordsInGrammarSnippet, DescriptionToGrammarTypeName -> DescriptionToGrammarSnippet, - GrammarToCNFTypeName -> GrammarToCNFSnippet + GrammarToCNFTypeName -> GrammarToCNFSnippet, + CYKTypeName -> CYKProblemSnippet ) ++ (if(Config.buchiGameSolving.enabled.get) { Map(BuchiSolvingTypeName -> BuchiGameSolving.SnippetAdapter) } else { Map[String, ProblemSnippet]() }) @@ -62,6 +65,7 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { 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/SolutionAttempt.scala b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala index f90fbff..e395c53 100644 --- a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala +++ b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala @@ -102,6 +102,17 @@ object GrammarToCNFSolutionAttempt extends GrammarToCNFSolutionAttempt with Long } +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/snippet/CYKProblemSnippet.scala b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala new file mode 100644 index 0000000..f373bce --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala @@ -0,0 +1,205 @@ +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 longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getCNFParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + + 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 longDescriptionField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "longdescfield") + + 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), 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, + "longdescription" -> longDescriptionField, + "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 longDescription : String = problem.getLongDescription + 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 longDescription = (formValuesXml \ "longdescfield").head.text + + val parsingErrors = GraderConnection.getCNFParsingErrors(grammar) + + if(parsingErrors.isEmpty) { + val specificProblem : CYKProblem = CYKProblem.create + + problem.setShortDescription(shortDescription).setLongDescription(longDescription).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 longDescriptionField = SHtml.textarea(longDescription, longDescription=_, "cols" -> "80", "rows" -> "1", "id" -> "longdescfield") + + 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), 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, + "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 = 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 =
Number of Words IN Grammar:
Number of Words NOT IN Grammar:
Grammar:
Short Description:
Long Description (will not be displayed...):
{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/WordsInGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala index c551d5a..ac90993 100644 --- a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala @@ -207,12 +207,12 @@ object WordsInGrammarSnippet extends ProblemSnippet { val insValXmlJs : StringBuilder = new StringBuilder("") for(i <- 0 to specificProblem.getInNeeded - 1) { - insValXmlJs.append("' + document.getElementById('wordinfield" + i.toString + "').value + '") + insValXmlJs.append("' + sanitizeInputForXML('wordinfield" + i.toString + "') + '") } insValXmlJs.append("") val outsValXmlJs : StringBuilder = new StringBuilder("") for(i <- 0 to specificProblem.getOutNeeded - 1) { - outsValXmlJs.append("' + document.getElementById('wordoutfield" + i.toString + "').value + '") + outsValXmlJs.append("' + sanitizeInputForXML('wordoutfield" + i.toString + "') + '") } outsValXmlJs.append("") diff --git a/src/main/webapp/cyk-problem/create.html b/src/main/webapp/cyk-problem/create.html new file mode 100644 index 0000000..1d5b0ed --- /dev/null +++ b/src/main/webapp/cyk-problem/create.html @@ -0,0 +1,42 @@ +
+

Create a new CYK Problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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

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

Edit a CYK Problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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..1a01093 --- /dev/null +++ b/src/main/webapp/cyk-problem/solve.html @@ -0,0 +1,52 @@ +
+ +

Solve a CYK problem

+ +

Grammar Syntax

+
    +
  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • +
  • 1 line per production
  • +
  • first variable is start variable
  • +
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
  • +
+ +

The Problem

+ + For the grammar with the productions:
+

+ + Perform the CYK Algorithm! + +

+ +
+
+ +

+ + +
+ + + + +
diff --git a/src/main/webapp/grammar-to-cnf-problem/create.html b/src/main/webapp/grammar-to-cnf-problem/create.html index 7338c8f..699726c 100644 --- a/src/main/webapp/grammar-to-cnf-problem/create.html +++ b/src/main/webapp/grammar-to-cnf-problem/create.html @@ -23,10 +23,6 @@

Problem Definition

- - - - diff --git a/src/main/webapp/grammar-to-cnf-problem/edit.html b/src/main/webapp/grammar-to-cnf-problem/edit.html index 7dab415..d0849cf 100644 --- a/src/main/webapp/grammar-to-cnf-problem/edit.html +++ b/src/main/webapp/grammar-to-cnf-problem/edit.html @@ -23,10 +23,6 @@

Problem Definition

- - - - diff --git a/src/main/webapp/javascript/grammarUtil.js b/src/main/webapp/javascript/grammarUtil.js index bf96d7e..8f26bd5 100644 --- a/src/main/webapp/javascript/grammarUtil.js +++ b/src/main/webapp/javascript/grammarUtil.js @@ -5,4 +5,11 @@ function formateGrammar(grammarString) { 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/words-in-grammar-problem/create.html b/src/main/webapp/words-in-grammar-problem/create.html index 2d61b46..a9dbd53 100644 --- a/src/main/webapp/words-in-grammar-problem/create.html +++ b/src/main/webapp/words-in-grammar-problem/create.html @@ -31,10 +31,6 @@

Problem Definition

- - - - diff --git a/src/main/webapp/words-in-grammar-problem/edit.html b/src/main/webapp/words-in-grammar-problem/edit.html index 784a6bc..f6be6be 100644 --- a/src/main/webapp/words-in-grammar-problem/edit.html +++ b/src/main/webapp/words-in-grammar-problem/edit.html @@ -31,10 +31,6 @@

Problem Definition

- - - - diff --git a/src/main/webapp/words-in-grammar-problem/solve.html b/src/main/webapp/words-in-grammar-problem/solve.html index a167e6e..3a96f21 100644 --- a/src/main/webapp/words-in-grammar-problem/solve.html +++ b/src/main/webapp/words-in-grammar-problem/solve.html @@ -1,4 +1,6 @@
+ +

Solve Words in Grammar problem

Grammar Syntax

From 675a42cc9990f6ec82fbdbb7a3db871a8e66b5a8 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Mon, 10 Jul 2017 21:27:10 +0200 Subject: [PATCH 06/25] updated gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 86daf73..f61ee35 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .settings/ console.devmode.log develop.db.mv.db +develop.db.trace.db project/project/ project/target/ target/ From ae9f64187c9d2e939d1b3f62114e9035148b2645 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Tue, 18 Jul 2017 20:53:08 +0200 Subject: [PATCH 07/25] removed input "long_description" in multiple exercises that dont use it + fixed creation/edition of multiple exercises + removed some debug outputs + removed db trace file --- develop.db.trace.db | 32 ------------------- .../snippet/CYKProblemSnippet.scala | 17 +++------- .../snippet/DescriptionToGrammarSnippet.scala | 2 -- .../snippet/GrammarToCNFSnippet.scala | 19 +++-------- .../snippet/WordsInGrammarSnippet.scala | 17 +++------- 5 files changed, 12 insertions(+), 75 deletions(-) delete mode 100644 develop.db.trace.db diff --git a/develop.db.trace.db b/develop.db.trace.db deleted file mode 100644 index 114655f..0000000 --- a/develop.db.trace.db +++ /dev/null @@ -1,32 +0,0 @@ -06-13 15:41:32 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] -06-13 15:41:32 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] -06-13 15:41:33 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] -06-13 15:41:33 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] -06-13 15:41:35 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] -06-13 15:41:35 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] -06-13 15:41:40 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] -06-13 15:41:40 jdbc[4]: exception -org.h2.jdbc.JdbcSQLException: Tabelle "WORDSINGRAMMARSOLUTIONATTEMPT" nicht gefunden -Table "WORDSINGRAMMARSOLUTIONATTEMPT" not found; SQL statement: -INSERT INTO wordsingrammarsolutionattempt (attemptwords,solutionattemptid) VALUES (?,?) [42102-182] diff --git a/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala index f373bce..c08bfe1 100644 --- a/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/CYKProblemSnippet.scala @@ -43,12 +43,11 @@ object CYKProblemSnippet extends ProblemSnippet { val grammar = (formValuesXml \ "grammarfield").head.text val word = (formValuesXml \ "wordfield").head.text val shortDescription = (formValuesXml \ "shortdescfield").head.text - val longDescription = (formValuesXml \ "longdescfield").head.text val parsingErrors = GraderConnection.getCNFParsingErrors(grammar) if(parsingErrors.isEmpty) { - val unspecificProblem = createUnspecificProb(shortDescription, longDescription) + val unspecificProblem = createUnspecificProb(shortDescription, shortDescription) val specificProblem : CYKProblem = CYKProblem.create specificProblem.problemId(unspecificProblem).grammar(grammar).word(word) @@ -63,14 +62,12 @@ object CYKProblemSnippet extends ProblemSnippet { 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 longDescriptionField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "longdescfield") 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" - val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), create(_)) + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + "'"), create(_)) val submit : JsCmd = hideSubmitButton & ajaxCall @@ -81,7 +78,6 @@ object CYKProblemSnippet extends ProblemSnippet { "grammarfield" -> grammarField, "wordfield" -> wordField, "shortdescription" -> shortDescriptionField, - "longdescription" -> longDescriptionField, "submit" -> submitButton) } @@ -92,7 +88,6 @@ object CYKProblemSnippet extends ProblemSnippet { val cykProblem = CYKProblem.findByGeneralProblem(problem) var shortDescription : String = problem.getShortDescription - var longDescription : String = problem.getLongDescription var grammar : String = cykProblem.getGrammar var word : String = cykProblem.getWord @@ -101,14 +96,13 @@ object CYKProblemSnippet extends ProblemSnippet { val grammar = (formValuesXml \ "grammarfield").head.text val word = (formValuesXml \ "wordfield").head.text val shortDescription = (formValuesXml \ "shortdescfield").head.text - val longDescription = (formValuesXml \ "longdescfield").head.text val parsingErrors = GraderConnection.getCNFParsingErrors(grammar) if(parsingErrors.isEmpty) { val specificProblem : CYKProblem = CYKProblem.create - problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() + problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save() cykProblem.grammar(grammar).word(word).save() returnFunc() } else { @@ -119,14 +113,12 @@ object CYKProblemSnippet extends ProblemSnippet { 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 longDescriptionField = SHtml.textarea(longDescription, longDescription=_, "cols" -> "80", "rows" -> "1", "id" -> "longdescfield") 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" - val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), edit(_)) + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + wordFieldValXmlJs + shortdescFieldValXmlJs + "'"), edit(_)) val submit : JsCmd = hideSubmitButton & ajaxCall @@ -137,7 +129,6 @@ object CYKProblemSnippet extends ProblemSnippet { "grammarfield" -> grammarField, "wordfield" -> wordField, "shortdescription" -> shortDescriptionField, - "longdescription" -> longDescriptionField, "submit" -> submitButton) } diff --git a/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala index cb8e510..a10c583 100644 --- a/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala @@ -70,8 +70,6 @@ object DescriptionToGrammarSnippet extends ProblemSnippet { val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), create(_)) val submit : JsCmd = hideSubmitButton & ajaxCall val submitButton : NodeSeq = - - println("test"); 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, diff --git a/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala index 7f7ab00..7b35a5a 100644 --- a/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala @@ -42,12 +42,11 @@ object GrammarToCNFSnippet extends ProblemSnippet { 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 unspecificProblem = createUnspecificProb(shortDescription, shortDescription) val specificProblem : GrammarToCNFProblem = GrammarToCNFProblem.create specificProblem.problemId(unspecificProblem).grammar(grammar) @@ -61,23 +60,18 @@ object GrammarToCNFSnippet extends ProblemSnippet { } 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 ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + "'"), create(_)) val submit : JsCmd = hideSubmitButton & ajaxCall val submitButton : NodeSeq = - - println("test"); 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, - "longdescription" -> longDescriptionField, "submit" -> submitButton) } @@ -88,21 +82,19 @@ object GrammarToCNFSnippet extends ProblemSnippet { val grammarToCNFProblem = GrammarToCNFProblem.findByGeneralProblem(problem) var shortDescription : String = problem.getShortDescription - var longDescription : String = problem.getLongDescription 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 longDescription = (formValuesXml \ "longdescfield").head.text val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) if(parsingErrors.isEmpty) { val specificProblem : GrammarToCNFProblem = GrammarToCNFProblem.create - problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() + problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save() grammarToCNFProblem.grammar(grammar).save() returnFunc() } else { @@ -112,13 +104,11 @@ object GrammarToCNFSnippet extends ProblemSnippet { 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 ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + shortdescFieldValXmlJs + "'"), edit(_)) val submit : JsCmd = hideSubmitButton & ajaxCall @@ -128,7 +118,6 @@ object GrammarToCNFSnippet extends ProblemSnippet { Helpers.bind("editform", template, "grammarfield" -> grammarField, "shortdescription" -> shortDescriptionField, - "longdescription" -> longDescriptionField, "submit" -> submitButton) } diff --git a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala index ac90993..98868d7 100644 --- a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala @@ -44,12 +44,11 @@ object WordsInGrammarSnippet extends ProblemSnippet { val inNeeded = (formValuesXml \ "inneededfield").head.text.toInt val outNeeded = (formValuesXml \ "outneededfield").head.text.toInt 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 unspecificProblem = createUnspecificProb(shortDescription, shortDescription) val specificProblem : WordsInGrammarProblem = WordsInGrammarProblem.create specificProblem.problemId(unspecificProblem).grammar(grammar).inNeeded(inNeeded).outNeeded(outNeeded) @@ -65,15 +64,13 @@ object WordsInGrammarSnippet extends ProblemSnippet { 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 longDescriptionField = SHtml.textarea("", value => {}, "cols" -> "80", "rows" -> "5", "id" -> "longdescfield") 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" - val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), create(_)) + 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 @@ -86,7 +83,6 @@ object WordsInGrammarSnippet extends ProblemSnippet { "inneededfield" -> inNeededField, "outneededfield" -> outNeededField, "shortdescription" -> shortDescriptionField, - "longdescription" -> longDescriptionField, "submit" -> submitButton) } @@ -97,7 +93,6 @@ object WordsInGrammarSnippet extends ProblemSnippet { val wordsInGrammarProblem = WordsInGrammarProblem.findByGeneralProblem(problem) var shortDescription : String = problem.getShortDescription - var longDescription : String = problem.getLongDescription var grammar : String = wordsInGrammarProblem.getGrammar var inNeeded : Int = wordsInGrammarProblem.getInNeeded var outNeeded : Int = wordsInGrammarProblem.getOutNeeded @@ -108,14 +103,13 @@ object WordsInGrammarSnippet extends ProblemSnippet { val inNeeded = (formValuesXml \ "inneededfield").head.text.toInt val outNeeded = (formValuesXml \ "outneededfield").head.text.toInt val shortDescription = (formValuesXml \ "shortdescfield").head.text - val longDescription = (formValuesXml \ "longdescfield").head.text val parsingErrors = GraderConnection.getGrammarParsingErrors(grammar) if(parsingErrors.isEmpty) { val specificProblem : WordsInGrammarProblem = WordsInGrammarProblem.create - problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() + problem.setShortDescription(shortDescription).setLongDescription(shortDescription).save() wordsInGrammarProblem.grammar(grammar).inNeeded(inNeeded).outNeeded(outNeeded).save() returnFunc() } else { @@ -127,15 +121,13 @@ object WordsInGrammarSnippet extends ProblemSnippet { 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 longDescriptionField = SHtml.textarea(longDescription, longDescription=_, "cols" -> "80", "rows" -> "1", "id" -> "longdescfield") 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 longdescFieldValXmlJs : String = "' + document.getElementById('longdescfield').value + '" - val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + longdescFieldValXmlJs + "'"), edit(_)) + val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("'" + grammarFieldValXmlJs + inNeededFieldValXmlJs + outNeededFieldValXmlJs + shortdescFieldValXmlJs + "'"), edit(_)) val submit : JsCmd = hideSubmitButton & ajaxCall @@ -147,7 +139,6 @@ object WordsInGrammarSnippet extends ProblemSnippet { "inneededfield" -> inNeededField, "outneededfield" -> outNeededField, "shortdescription" -> shortDescriptionField, - "longdescription" -> longDescriptionField, "submit" -> submitButton) } From c53f7b3b08c5bca8f274061491e55b6cda070434 Mon Sep 17 00:00:00 2001 From: Martin Helfrich Date: Sat, 22 Jul 2017 16:50:16 +0200 Subject: [PATCH 08/25] Update SOAPConnection.scala to revert hotfix --- src/main/scala/com/automatatutor/lib/SOAPConnection.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala index ed77af3..a1745d6 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -166,7 +166,6 @@ object GraderConnection { } def getRegexParsingErrors(potentialRegex : String, alphabet : Seq[String]) : Seq[String] = { - return List() val arguments = Map[String, Node]( "regexDesc" ->
{ potentialRegex }
, "alphabet" ->
{ alphabet.map((symbol : String) => Elem(null, "symbol", Null, TopScope, true, Text(symbol))) }
) From ce906f023fa297cb32ccae6db0ce781bc7818a46 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Sun, 23 Jul 2017 11:45:51 +0200 Subject: [PATCH 09/25] Auch alles! --- .gitignore | 2 + src/main/scala/bootstrap/liftweb/Boot.scala | 1 + .../automatatutor/lib/SOAPConnection.scala | 21 +++++- .../com/automatatutor/model/Problem.scala | 5 +- .../model/ProductConstructionProblem.scala | 32 +++++--- .../automatatutor/model/SolutionAttempt.scala | 22 +++++- .../snippet/ProductConstructionSnippet.scala | 73 ++++++++++++++----- .../com/automatatutor/snippet/Users.scala | 7 +- .../javascript/includeProductConstruction.js | 5 ++ src/main/webapp/javascript/interface.js | 13 ++++ .../webapp/product-construction/create.html | 33 +++++++-- .../webapp/product-construction/edit.html | 27 ++++--- .../webapp/product-construction/solve.html | 11 +-- 13 files changed, 197 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 86daf73..07f7164 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ 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 44557c5..d458618 100644 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -41,6 +41,7 @@ class Boot { User, Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType, DFAConstructionProblem, DFAConstructionSolutionAttempt, ProductConstructionProblem, ProductConstructionSolutionAttempt, + MinimizationProblem, MinimizationSolutionAttempt, NFAConstructionProblem, NFAConstructionSolutionAttempt, NFAToDFAProblem, NFAToDFASolutionAttempt, RegExConstructionProblem, RegexConstructionSolutionAttempt, diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala index 60bad51..15cc8c0 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -121,7 +121,7 @@ object GraderConnection { //Product Construction - def getProductConstructionFeedback(correctDfaDescriptionList : List[String], attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = { + 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() @@ -131,12 +131,31 @@ object GraderConnection { 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: Adjust to work for Minimization + def getMinimizationFeedback(dfaDescription : String, attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = { + + val arguments = Map[String, Node]( + "dfaDesc" -> XML.loadString(dfaDescription), + "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")) } diff --git a/src/main/scala/com/automatatutor/model/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala index e0d48a9..cf30bc8 100644 --- a/src/main/scala/com/automatatutor/model/Problem.scala +++ b/src/main/scala/com/automatatutor/model/Problem.scala @@ -25,13 +25,15 @@ class ProblemType extends LongKeyedMapper[ProblemType] with IdPK { //val PLTypeName = "Pumping Lemma Proof" val BuchiSolvingTypeName = "Buchi Game Solving" val ProductConstructionTypeName = "Product Construction" + val MinimizationTypeName = "Minimization" val knownProblemTypes : Map[String, ProblemSnippet] = Map( DFAConstructionTypeName -> DFAConstructionSnippet, NFAConstructionTypeName -> NFAProblemSnippet, NFAToDFATypeName -> NFAToDFAProblemSnippet, EnglishToRegExTypeName -> RegExConstructionSnippet, - ProductConstructionTypeName -> ProductConstructionSnippet + ProductConstructionTypeName -> ProductConstructionSnippet, + MinimizationTypeName -> MinimizationSnippet ) ++ (if(Config.buchiGameSolving.enabled.get) { Map(BuchiSolvingTypeName -> BuchiGameSolving.SnippetAdapter) } else { Map[String, ProblemSnippet]() }) @@ -45,6 +47,7 @@ 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) diff --git a/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala b/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala index 46f1eb1..a7bc9ac 100644 --- a/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala +++ b/src/main/scala/com/automatatutor/model/ProductConstructionProblem.scala @@ -15,23 +15,37 @@ class ProductConstructionProblem extends LongKeyedMapper[ProductConstructionProb def getSingleton = ProductConstructionProblem protected object problemId extends MappedLongForeignKey(this, Problem) - protected object automaton extends MappedText(this) - + 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 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 getXmlDescription1 : NodeSeq = XML.loadString(this.automataList(0).is) + def getXmlDescription2 : NodeSeq = XML.loadString(this.automataList(1).is) - def getAlphabet : Seq[String] = (getXmlDescription \ "alphabet" \ "symbol").map(_.text) + def getAlphabet : Seq[String] = (getXmlDescription1 \ "alphabet" \ "symbol").map(_.text) override def copy(): ProductConstructionProblem = { val retVal = new ProductConstructionProblem retVal.problemId(this.problemId.get) - retVal.automaton(this.automaton.get) + retVal.automataList(0)(this.automataList(0).get) return retVal } } diff --git a/src/main/scala/com/automatatutor/model/SolutionAttempt.scala b/src/main/scala/com/automatatutor/model/SolutionAttempt.scala index 9025625..6ec2a0a 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 @@ -35,6 +36,7 @@ object DFAConstructionSolutionAttempt extends DFAConstructionSolutionAttempt wit } } +//Product Construction class ProductConstructionSolutionAttempt extends LongKeyedMapper[ProductConstructionSolutionAttempt] with IdPK { def getSingleton = ProductConstructionSolutionAttempt @@ -44,10 +46,25 @@ class ProductConstructionSolutionAttempt extends LongKeyedMapper[ProductConstruc 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 DFA construction attempt" + 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 @@ -59,6 +76,7 @@ object NFAConstructionSolutionAttempt extends NFAConstructionSolutionAttempt wit } +//NFA to DFA class NFAToDFASolutionAttempt extends LongKeyedMapper[NFAToDFASolutionAttempt] with IdPK { def getSingleton = NFAToDFASolutionAttempt @@ -70,6 +88,7 @@ object NFAToDFASolutionAttempt extends NFAToDFASolutionAttempt with LongKeyedMet } +//Regex Construction class RegexConstructionSolutionAttempt extends LongKeyedMapper[RegexConstructionSolutionAttempt] with IdPK { def getSingleton = RegexConstructionSolutionAttempt @@ -81,6 +100,7 @@ object RegexConstructionSolutionAttempt extends RegexConstructionSolutionAttempt } +//Pumping Lemma class PumpingLemmaSolutionAttempt extends LongKeyedMapper[PumpingLemmaSolutionAttempt] with IdPK { def getSingleton = PumpingLemmaSolutionAttempt diff --git a/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala index a404491..a0e9608 100644 --- a/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala @@ -42,28 +42,38 @@ object ProductConstructionSnippet extends ProblemSnippet { var shortDescription : String = "" var longDescription : String = "" - var automaton : 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).setAutomaton(automaton) + 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 automatonField = SHtml.hidden(automatonXml => automaton = preprocessAutomatonXml(automatonXml), "", "id" -> "automatonField") + 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 submitButton = SHtml.submit("Create", create, "onClick" -> "document.getElementById('automatonField').value = Editor.canvas.exportAutomaton()") + 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, - "automaton" -> automatonField, + "automaton1" -> automaton1Field, + "automaton2" -> automaton2Field, + "boolop" -> booleanOperationField, "shortdescription" -> shortDescriptionField, "longdescription" -> longDescriptionField, "submit" -> submitButton) @@ -77,25 +87,41 @@ object ProductConstructionSnippet extends ProblemSnippet { var shortDescription : String = problem.getShortDescription var longDescription : String = problem.getLongDescription - var automaton : String = "" + var booleanOperation : String = productConstructionProblem.getBooleanOperation.toString() + var automaton1 : String = "" + var automaton2 : String = "" def create() = { problem.setShortDescription(shortDescription).setLongDescription(longDescription).save() - productConstructionProblem.setAutomaton(automaton).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 automatonField = SHtml.hidden(automatonXml => automaton = preprocessAutomatonXml(automatonXml), "", "id" -> "automatonField") + 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 submitButton = SHtml.submit("Edit", create, "onClick" -> "document.getElementById('automatonField').value = Editor.canvas.exportAutomaton()") - val setupScript = + 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, - "automaton" -> automatonField, + "automaton1" -> automaton1Field, + "automaton2" -> automaton2Field, "setupscript" -> setupScript, + "boolop" -> booleanOperationField, "shortdescription" -> shortDescriptionField, "longdescription" -> longDescriptionField, "submit" -> submitButton) @@ -118,10 +144,12 @@ object ProductConstructionSnippet extends ProblemSnippet { val attemptDfaXml = XML.loadString(attemptDfaDescription) //TODO - Different Automata - val correctDfaDescription = productConstructionProblem.getXmlDescription.toString + val correctDfaDescription1 = productConstructionProblem.getXmlDescription1.toString + val correctDfaDescription2 = productConstructionProblem.getXmlDescription2.toString + val booleanOperation = productConstructionProblem.getBooleanOperation.toString() val attemptTime = Calendar.getInstance.getTime() - val x = List(correctDfaDescription, correctDfaDescription) - val graderResponse = GraderConnection.getProductConstructionFeedback(x, attemptDfaDescription, maxGrade.toInt) + val x = List(correctDfaDescription1, correctDfaDescription2) + val graderResponse = GraderConnection.getProductConstructionFeedback(x, attemptDfaDescription, booleanOperation, maxGrade.toInt) val numericalGrade = graderResponse._1 val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) @@ -139,23 +167,30 @@ object ProductConstructionSnippet extends ProblemSnippet { } val problemAlphabet = productConstructionProblem.getAlphabet + val automataList = productConstructionProblem.getAutomataList val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]" - val alphabetScript : NodeSeq = + 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.canvas.exportAutomaton()"), grade(_)) + 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, - "alphabetscript" -> alphabetScript, + "setupscript" -> setupScript, "alphabettext" -> problemAlphabetNodeSeq, - "problemdescription" -> problemDescriptionNodeSeq, + "boolop" -> booleanOperationNodeSeq, "submitbutton" -> submitButton, "returnlink" -> returnLink)) } diff --git a/src/main/scala/com/automatatutor/snippet/Users.scala b/src/main/scala/com/automatatutor/snippet/Users.scala index f34c3fb..6727603 100644 --- a/src/main/scala/com/automatatutor/snippet/Users.scala +++ b/src/main/scala/com/automatatutor/snippet/Users.scala @@ -9,6 +9,8 @@ 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 @@ -117,9 +119,10 @@ class Users extends PaginatorSnippet[User] { def resetlink(ignored : NodeSeq) : NodeSeq = { def resetDatabase() = { List(Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType, - DFAConstructionProblem, ProductConstructionProblem, NFAConstructionProblem, + DFAConstructionProblem, ProductConstructionProblem, MinimizationProblem, NFAConstructionProblem, NFAToDFAProblem, NFAToDFASolutionAttempt, - ProblemSet, SolutionAttempt, Supervision, DFAConstructionSolutionAttempt, ProductConstructionSolutionAttempt, 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/webapp/javascript/includeProductConstruction.js b/src/main/webapp/javascript/includeProductConstruction.js index b5060f0..4bf689a 100644 --- a/src/main/webapp/javascript/includeProductConstruction.js +++ b/src/main/webapp/javascript/includeProductConstruction.js @@ -29,6 +29,11 @@ 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/interface.js b/src/main/webapp/javascript/interface.js index 342c79b..1877b06 100644 --- a/src/main/webapp/javascript/interface.js +++ b/src/main/webapp/javascript/interface.js @@ -45,6 +45,19 @@ $.SvgCanvas = function(container, config, style) { acceptanceMarker: 'css' }, + 'prodaut': { + node: { + label: 'id' + }, + transition: { + labeled: true, + deterministic: true + }, + hasInitialNode: true, + twoPlayers: false, + acceptanceMarker: 'css' + }, + 'nondetaut': { node: { label: 'id' diff --git a/src/main/webapp/product-construction/create.html b/src/main/webapp/product-construction/create.html index f6c789b..6375754 100644 --- a/src/main/webapp/product-construction/create.html +++ b/src/main/webapp/product-construction/create.html @@ -6,20 +6,29 @@ function setAlphabet() { var alphabet = parseAlphabet(); if(alphabetChecks(alphabet)){ - Editor.canvas.setAlphabet(alphabet); + Editor.canvasDfa1.setAlphabet(alphabet); + Editor.canvasDfa2.setAlphabet(alphabet); } } - -

Create a new DFA-construction problem

+ //function setNumberOfAutomata() { + // var number = document.getElementById('noAutomata').value; + // setNumberOfCanvas(number) + //} + + +

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: +
- + -
Grammar:
Word:
Short Description:
Short Description:
Long Description (will not be displayed):
Short Description:
Long Description (will not be displayed...):
Short Description:
Long Description (will not be displayed):
Short Description:
Long Description (will not be displayed...):
+
- + + + + + + + + + - + diff --git a/src/main/webapp/product-construction/edit.html b/src/main/webapp/product-construction/edit.html index 968e5a3..9c9ee61 100644 --- a/src/main/webapp/product-construction/edit.html +++ b/src/main/webapp/product-construction/edit.html @@ -6,15 +6,16 @@ function setAlphabet() { var alphabet = parseAlphabet(); if(alphabetChecks(alphabet)){ - Editor.canvas.setAlphabet(alphabet); + Editor.canvasDfa1.setAlphabet(alphabet); + Editor.canvasDfa2.setAlphabet(alphabet); } } - function setNumberOfAutomata() { - var number = document.getElementById('noAutomata').value; - setNumberOfCanvas(number) - } - + //function setNumberOfAutomata() { + // var number = document.getElementById('noAutomata').value; + // setNumberOfCanvas(number) + //} +

Edit product-construction problem

@@ -27,14 +28,22 @@

Edit product-construction problem


- + -
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 index 91872da..fa51025 100644 --- a/src/main/webapp/product-construction/solve.html +++ b/src/main/webapp/product-construction/solve.html @@ -1,16 +1,17 @@

Solve product-construction problem

- Construct an automaton that recognizes the following language of strings over the alphabet - :
- + Combine the given automata via product construction + :
+ using the boolean operation +

- +
- + From e040b2a9c97c34ff220c01ec29d1f99d8750b205 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Sun, 23 Jul 2017 11:46:59 +0200 Subject: [PATCH 10/25] MinimizationProblem.scala --- .../model/MinimizationProblem.scala | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/main/scala/com/automatatutor/model/MinimizationProblem.scala 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 From b3b9e1cebf8640577637cabdf24769ace11cc41d Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Sun, 23 Jul 2017 11:47:46 +0200 Subject: [PATCH 11/25] MinimizationSnippet.scala --- .../snippet/MinimizationSnippet.scala | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala 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..a63ae1a --- /dev/null +++ b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala @@ -0,0 +1,168 @@ +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( 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) + val correctDfaDescription = minimizationProblem.getXmlDescription.toString + val attemptTime = Calendar.getInstance.getTime() + val x = List(correctDfaDescription, correctDfaDescription) + //TODO: Implement getMinimizationFeedback() + val graderResponse = GraderConnection.getMinimizationFeedback(correctDfaDescription, 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 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("Editor.canvasSol.exportAutomaton()"), 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, + "setupscript" -> setupScript, + "alphabettext" -> problemAlphabetNodeSeq, + "problemdescription" -> problemDescriptionNodeSeq, + "submitbutton" -> submitButton, + "returnlink" -> returnLink)) + } + + override def onDelete( generalProblem : Problem ) : Unit = { + MinimizationProblem.deleteByGeneralProblem(generalProblem) + } +} From 258ca586580748270f5d2764304f125bb0e44b43 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Sun, 23 Jul 2017 11:48:28 +0200 Subject: [PATCH 12/25] minimization folder --- .../webapp/minimization/applet-solve.html | 39 +++++++++++++ src/main/webapp/minimization/applet.html | 33 +++++++++++ src/main/webapp/minimization/create.html | 57 +++++++++++++++++++ src/main/webapp/minimization/edit.html | 52 +++++++++++++++++ src/main/webapp/minimization/solve.html | 29 ++++++++++ 5 files changed, 210 insertions(+) create mode 100644 src/main/webapp/minimization/applet-solve.html create mode 100644 src/main/webapp/minimization/applet.html create mode 100644 src/main/webapp/minimization/create.html create mode 100644 src/main/webapp/minimization/edit.html create mode 100644 src/main/webapp/minimization/solve.html diff --git a/src/main/webapp/minimization/applet-solve.html b/src/main/webapp/minimization/applet-solve.html new file mode 100644 index 0000000..ff81fa7 --- /dev/null +++ b/src/main/webapp/minimization/applet-solve.html @@ -0,0 +1,39 @@ +
+ + + + + + + + + + + + + + +
+ +
+
+
+
+ +
+ + + +
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: +
+ + + + + + + +
Boolean Operation:
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..9911b11 --- /dev/null +++ b/src/main/webapp/minimization/solve.html @@ -0,0 +1,29 @@ +
+

Solve minimization problem

+
+ Construct an automaton that recognizes the following language of strings over the alphabet + :
+ +
+
+ + + +
+ + + + + + + + + + +
From cd9c6645d4c7298738e1145960fff26bdda9a6d8 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Sun, 23 Jul 2017 11:50:09 +0200 Subject: [PATCH 13/25] More unversioned files --- .../includeMinimizationCreateEdit.js | 17 +++++++ .../javascript/includeMinimizationSolve.js | 23 ++++++++++ .../includeProductConstructionCreateEdit.js | 23 ++++++++++ .../includeProductConstructionSolve.js | 29 ++++++++++++ .../applet-create-edit.html | 40 +++++++++++++++++ .../applet-solve-product.html | 44 +++++++++++++++++++ 6 files changed, 176 insertions(+) create mode 100644 src/main/webapp/javascript/includeMinimizationCreateEdit.js create mode 100644 src/main/webapp/javascript/includeMinimizationSolve.js create mode 100644 src/main/webapp/javascript/includeProductConstructionCreateEdit.js create mode 100644 src/main/webapp/javascript/includeProductConstructionSolve.js create mode 100644 src/main/webapp/product-construction/applet-create-edit.html create mode 100644 src/main/webapp/product-construction/applet-solve-product.html 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..e0dd8c2 --- /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, 'detaut'); + } +} + +$(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..9f5cda9 --- /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("#svgcanvasdfa1", Editor.curConfigDfa1, 'detaut'); + } + if(!Editor.canvasDfa2) { + Editor.canvasDfa2 = new $.SvgCanvas("#svgcanvasdfa2", 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..1b8b942 --- /dev/null +++ b/src/main/webapp/javascript/includeProductConstructionSolve.js @@ -0,0 +1,29 @@ +var Editor = { + curConfigDfa1: { + dimensions: [740,480] + }, + curConfigDfa2: { + dimensions: [740,480] + }, + 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/product-construction/applet-create-edit.html b/src/main/webapp/product-construction/applet-create-edit.html new file mode 100644 index 0000000..6331ae7 --- /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..6a7cec0 --- /dev/null +++ b/src/main/webapp/product-construction/applet-solve-product.html @@ -0,0 +1,44 @@ +
+ + + + + + + + + + + + + + +
+ +
+
+
+
+
+ +
+ + + + +
+ From 4ead4dee3c389913e86a4c250c323a496b4aea41 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Sun, 23 Jul 2017 12:34:28 +0200 Subject: [PATCH 14/25] imports cleaned up --- src/main/scala/com/automatatutor/model/Problem.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/scala/com/automatatutor/model/Problem.scala b/src/main/scala/com/automatatutor/model/Problem.scala index 2b40c06..ee714b0 100644 --- a/src/main/scala/com/automatatutor/model/Problem.scala +++ b/src/main/scala/com/automatatutor/model/Problem.scala @@ -5,13 +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 com.automatatutor.snippet.WordsInGrammarSnippet import com.automatatutor.snippet.DescriptionToGrammarSnippet import com.automatatutor.snippet.CYKProblemSnippet import com.automatatutor.snippet.GrammarToCNFSnippet -import com.automatatutor.snippet._ -import net.liftweb.common.Empty +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 From 2c0d6d6c8434ee36f51ddae461d053048d9d64f3 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Mon, 24 Jul 2017 19:46:27 +0200 Subject: [PATCH 15/25] return link in practice problems fixed --- src/main/scala/com/automatatutor/snippet/Courses.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/com/automatatutor/snippet/Courses.scala b/src/main/scala/com/automatatutor/snippet/Courses.scala index fb81fcf..4b8ccf4 100644 --- a/src/main/scala/com/automatatutor/snippet/Courses.scala +++ b/src/main/scala/com/automatatutor/snippet/Courses.scala @@ -462,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 = { From 25c215dc1525eeca73b1399a9baee29619d81237 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Tue, 25 Jul 2017 07:39:41 +0200 Subject: [PATCH 16/25] states of product automata named as tuples --- .../snippet/NFAToDFAProblemSnippet.scala | 7 +- .../snippet/ProductConstructionSnippet.scala | 3 +- src/main/webapp/javascript/includeDFANFA.js | 2 +- src/main/webapp/javascript/interface.js | 227 ++++++++++++++++-- 4 files changed, 220 insertions(+), 19 deletions(-) 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/ProductConstructionSnippet.scala b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala index a0e9608..0a8b3c8 100644 --- a/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/ProductConstructionSnippet.scala @@ -174,7 +174,8 @@ object ProductConstructionSnippet extends ProblemSnippet { val problemAlphabetNodeSeq = Text("{" + problemAlphabet.mkString(",") + "}") 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/interface.js b/src/main/webapp/javascript/interface.js index 1877b06..74009b5 100644 --- a/src/main/webapp/javascript/interface.js +++ b/src/main/webapp/javascript/interface.js @@ -45,17 +45,33 @@ $.SvgCanvas = function(container, config, style) { acceptanceMarker: 'css' }, - 'prodaut': { - node: { - label: 'id' - }, - transition: { - labeled: true, - deterministic: true - }, - hasInitialNode: true, - twoPlayers: false, - 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': { @@ -124,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)} @@ -209,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') @@ -329,6 +467,10 @@ $.SvgCanvas = function(container, config, style) { var solveMode = false + //for product construction + var numberOfNodesOfAutomaton1 = 4, + numberOfNodesOfAutomaton2 = 4; + // init D3 force layout var force; @@ -798,6 +940,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 @@ -965,6 +1122,7 @@ $.SvgCanvas = function(container, config, style) { restart(); } + //rim of regular node g.append('svg:circle') .attr('class', 'node') .attr('id', 'main') @@ -1031,6 +1189,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 @@ -1070,8 +1243,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) @@ -1768,6 +1945,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, + right: 0, + states: [], initial: false, accepting: false, reflexiveNum: reflNum, @@ -2233,6 +2413,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 * From 842370b653ce49d34c3328ee090eda3e58ad1cc8 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Mon, 14 Aug 2017 19:08:36 +0200 Subject: [PATCH 17/25] minor bugfixes (negativ grades when parsing error, sanitizeInput edited) --- src/main/scala/com/automatatutor/lib/SOAPConnection.scala | 2 +- .../automatatutor/snippet/DescriptionToGrammarSnippet.scala | 6 ++++-- .../com/automatatutor/snippet/GrammarToCNFSnippet.scala | 6 ++++-- src/main/webapp/javascript/grammarUtil.js | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala index 590eba1..7347cbb 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -268,7 +268,7 @@ object GraderConnection { "grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(attempt)) ); val responseXml1 = soapConnection.callMethod(namespace, "isCNF", arguments1) - if ((responseXml1 \ "res").head.text == "n") return (0, (responseXml1 \ "feedback")) + if ((responseXml1 \ "res").head.text == "n") return (-1, (responseXml1 \ "feedback")) val arguments2 = Map[String, Node]( "solution" -> Elem(null, "solution", Null, TopScope, true, Text(solution)), diff --git a/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala index a10c583..e608a91 100644 --- a/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/DescriptionToGrammarSnippet.scala @@ -144,7 +144,7 @@ object DescriptionToGrammarSnippet extends ProblemSnippet { val gradeAndFeedback = GraderConnection.getDescriptionToGrammarFeedback(specificProblem.grammar.is, attemptGrammar, maxGrade.toInt) - val numericalGrade = gradeAndFeedback._1 + var numericalGrade = gradeAndFeedback._1 val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) // Only save the specific attempt if we saved the general attempt and grammar was parseable @@ -152,7 +152,9 @@ object DescriptionToGrammarSnippet extends ProblemSnippet { DescriptionToGrammarSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptGrammar(attemptGrammar).save } - val setNumericalGrade : JsCmd = SetHtml("grade", Text(gradeAndFeedback._1.toString + "/" + maxGrade.toString)) + 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") diff --git a/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala index 7b35a5a..624c123 100644 --- a/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/GrammarToCNFSnippet.scala @@ -136,7 +136,7 @@ object GrammarToCNFSnippet extends ProblemSnippet { //TODO val gradeAndFeedback = GraderConnection.getGrammarToCNFFeedback(specificProblem.grammar.is, attemptGrammar, maxGrade.toInt) - val numericalGrade = gradeAndFeedback._1 + var numericalGrade = gradeAndFeedback._1 val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) // Only save the specific attempt if we saved the general attempt and grammar was parseable @@ -144,7 +144,9 @@ object GrammarToCNFSnippet extends ProblemSnippet { GrammarToCNFSolutionAttempt.create.solutionAttemptId(generalAttempt).attemptGrammar(attemptGrammar).save } - val setNumericalGrade : JsCmd = SetHtml("grade", Text(gradeAndFeedback._1.toString + "/" + maxGrade.toString)) + 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") diff --git a/src/main/webapp/javascript/grammarUtil.js b/src/main/webapp/javascript/grammarUtil.js index 8f26bd5..3daa55e 100644 --- a/src/main/webapp/javascript/grammarUtil.js +++ b/src/main/webapp/javascript/grammarUtil.js @@ -10,6 +10,6 @@ function formateGrammar(grammarString) { function sanitizeInputForXML(id) { var input = document.getElementById(id); if (input === null) return ""; - input.value = input.value.replace(/[<>]/g, ""); + input.value = input.value.replace(/[<>&]/g, ""); return input.value; } \ No newline at end of file From 03eef77770c85729089cfa2c704086d2f8dd36fa Mon Sep 17 00:00:00 2001 From: ga63nij Date: Mon, 14 Aug 2017 19:28:07 +0200 Subject: [PATCH 18/25] WordsInGrammar: limit max. word length to prevent server overload --- .../com/automatatutor/snippet/WordsInGrammarSnippet.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala index 98868d7..5c0484a 100644 --- a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala @@ -187,12 +187,12 @@ object WordsInGrammarSnippet extends ProblemSnippet { val outNeededText = Text("" + specificProblem.outNeeded) val wordsInFields = new Array[NodeSeq](specificProblem.getInNeeded) for(i <- 0 to specificProblem.getInNeeded - 1) { - wordsInFields(i) = SHtml.text("", value => {}, "id" -> ("wordinfield" + i.toString)) + wordsInFields(i) = SHtml.text("", value => {}, "id" -> ("wordinfield" + i.toString), "maxlength" -> "75") } val wordsInFieldNodeSeq =
    {wordsInFields.map(i =>
  • {i}
  • )}
val wordsOutFields = new Array[NodeSeq](specificProblem.getOutNeeded) for(i <- 0 to specificProblem.getOutNeeded - 1) { - wordsOutFields(i) = SHtml.text("", value => {}, "id" -> ("wordoutfield" + i.toString)) + wordsOutFields(i) = SHtml.text("", value => {}, "id" -> ("wordoutfield" + i.toString), "maxlength" -> "75") } val wordsOutFieldNodeSeq =
    {wordsOutFields.map(i =>
  • {i}
  • )}
From 259d260d01ecc8dd95c37cc15d44b1c268612e60 Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Mon, 28 Aug 2017 21:02:17 +0200 Subject: [PATCH 19/25] Product Construction, Minimization - Layout updates --- src/main/scala/bootstrap/liftweb/Boot.scala | 2 +- .../automatatutor/lib/SOAPConnection.scala | 5 +- .../com/automatatutor/snippet/Courses.scala | 34 +++++++-- .../snippet/MinimizationSnippet.scala | 30 +++++++- .../snippet/MinimizationTable.scala | 69 +++++++++++++++++++ src/main/webapp/courses/index.html | 2 +- .../javascript/includeMinimizationSolve.js | 2 +- .../includeProductConstructionCreateEdit.js | 4 +- .../includeProductConstructionSolve.js | 4 +- src/main/webapp/javascript/interface.js | 42 +++++++++-- .../webapp/minimization/applet-solve.html | 40 +---------- src/main/webapp/minimization/solve.html | 60 +++++++++++++++- .../applet-create-edit.html | 4 +- .../applet-solve-product.html | 6 +- src/main/webapp/stylesheets/proto2.css | 56 +++++++++++++++ 15 files changed, 294 insertions(+), 66 deletions(-) create mode 100644 src/main/scala/com/automatatutor/snippet/MinimizationTable.scala diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala index 27e62e8..ab524de 100644 --- a/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -68,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 590eba1..d7c19ca 100644 --- a/src/main/scala/com/automatatutor/lib/SOAPConnection.scala +++ b/src/main/scala/com/automatatutor/lib/SOAPConnection.scala @@ -143,11 +143,12 @@ object GraderConnection { // Minimization - //TODO: Adjust to work for Minimization - def getMinimizationFeedback(dfaDescription : String, attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = { + //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")), diff --git a/src/main/scala/com/automatatutor/snippet/Courses.scala b/src/main/scala/com/automatatutor/snippet/Courses.scala index 4b8ccf4..08c19d4 100644 --- a/src/main/scala/com/automatatutor/snippet/Courses.scala +++ b/src/main/scala/com/automatatutor/snippet/Courses.scala @@ -68,6 +68,32 @@ class Courses { return attendedCoursesNodeSeq ++ supervisedCoursesNodeSeq ++ createCourseLink } + def showall2(ignored : NodeSeq) : NodeSeq = { + + val attendedCourses = User.currentUser.map(_.getAttendedCourses) openOr List() + val attendedCoursesNodeSeq = if (!attendedCourses.isEmpty) { +

Attended Courses

++ { displayAttendedCourses(attendedCourses) } + } else { +

Där är inga kurser!

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

Supervised Courses

++ { displaySupervisedCourses(supervisedCourses) } + } 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 + } + def renderSolvePracticeSet( practiceSet : ProblemSet ) : NodeSeq = { val posedProblems = practiceSet.getPosedProblems @@ -316,7 +342,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 { @@ -329,7 +355,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) @@ -405,8 +431,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 { diff --git a/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala index a63ae1a..ee58ab1 100644 --- a/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala @@ -104,7 +104,11 @@ object MinimizationSnippet extends ProblemSnippet { val minimizationProblem = MinimizationProblem.findByGeneralProblem(generalProblem) - def grade( attemptDfaDescription : String ) : JsCmd = { + def grade(minimizationTableDescription : String /*, attemptDfaDescription : String*/ ) : JsCmd = { + + //Temporary: + val attemptDfaDescription = "" + if(remainingAttempts() <= 0) { return JsShowId("feedbackdisplay") & SetHtml("feedbackdisplay", @@ -118,7 +122,7 @@ object MinimizationSnippet extends ProblemSnippet { val attemptTime = Calendar.getInstance.getTime() val x = List(correctDfaDescription, correctDfaDescription) //TODO: Implement getMinimizationFeedback() - val graderResponse = GraderConnection.getMinimizationFeedback(correctDfaDescription, attemptDfaDescription, maxGrade.toInt) + val graderResponse = GraderConnection.getMinimizationFeedback(correctDfaDescription, minimizationTableDescription, attemptDfaDescription, maxGrade.toInt) val numericalGrade = graderResponse._1 val generalAttempt = recordSolutionAttempt(numericalGrade, attemptTime) @@ -137,6 +141,25 @@ object MinimizationSnippet extends ProblemSnippet { } val problemAlphabet = minimizationProblem.getAlphabet + val automaton = XML.loadString(minimizationProblem.getAutomaton) + val n = ((automaton \ "stateSet") \ "_").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, i+1) + } + } + val minimizationTable : NodeSeq =
Short Description:
Long Description (will appear in the problem in the form of "Construct a DFA that recognizes the following language: {long description}"):
{cyk.map(row => + {row.map(col => + )} + )}
+ { + SHtml.text("", value => {}, "class" -> "", "start" -> col._1.toString(), "end" -> col._2.toString(), "size" -> "5") + }{ + Text("(" + col._1.toString() + "," + col._2.toString() + ")") } + +
val alphabetJavaScriptArray = "[\"" + problemAlphabet.mkString("\",\"") + "\"]" val setupScript : NodeSeq = @@ -149,12 +172,13 @@ object MinimizationSnippet extends ProblemSnippet { val problemDescriptionNodeSeq = Text(generalProblem.getLongDescription) val hideSubmitButton : JsCmd = JsHideId("submitbutton") - val ajaxCall : JsCmd = SHtml.ajaxCall(JsRaw("Editor.canvasSol.exportAutomaton()"), grade(_)) + 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, 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/webapp/courses/index.html b/src/main/webapp/courses/index.html index 414ef86..5ab6f51 100644 --- a/src/main/webapp/courses/index.html +++ b/src/main/webapp/courses/index.html @@ -4,7 +4,7 @@
- +

Enroll in course

diff --git a/src/main/webapp/javascript/includeMinimizationSolve.js b/src/main/webapp/javascript/includeMinimizationSolve.js index e0dd8c2..dea2fc1 100644 --- a/src/main/webapp/javascript/includeMinimizationSolve.js +++ b/src/main/webapp/javascript/includeMinimizationSolve.js @@ -18,6 +18,6 @@ function initCanvas() { $(document).ready(function() { initCanvas(); -}); +}); diff --git a/src/main/webapp/javascript/includeProductConstructionCreateEdit.js b/src/main/webapp/javascript/includeProductConstructionCreateEdit.js index 9f5cda9..4d2ad51 100644 --- a/src/main/webapp/javascript/includeProductConstructionCreateEdit.js +++ b/src/main/webapp/javascript/includeProductConstructionCreateEdit.js @@ -9,10 +9,10 @@ var Editor = { function initCanvas() { if(!Editor.canvasDfa1) { - Editor.canvasDfa1 = new $.SvgCanvas("#svgcanvasdfa1", Editor.curConfigDfa1, 'detaut'); + Editor.canvasDfa1 = new $.SvgCanvas("#svgcanvascreatedfa1", Editor.curConfigDfa1, 'detaut'); } if(!Editor.canvasDfa2) { - Editor.canvasDfa2 = new $.SvgCanvas("#svgcanvasdfa2", Editor.curConfigDfa2, 'detaut'); + Editor.canvasDfa2 = new $.SvgCanvas("#svgcanvascreatedfa2", Editor.curConfigDfa2, 'detaut'); } } diff --git a/src/main/webapp/javascript/includeProductConstructionSolve.js b/src/main/webapp/javascript/includeProductConstructionSolve.js index 1b8b942..b43faa9 100644 --- a/src/main/webapp/javascript/includeProductConstructionSolve.js +++ b/src/main/webapp/javascript/includeProductConstructionSolve.js @@ -1,9 +1,9 @@ var Editor = { curConfigDfa1: { - dimensions: [740,480] + dimensions: [360,240] }, curConfigDfa2: { - dimensions: [740,480] + dimensions: [360,240] }, curConfigDfaSol: { dimensions: [740,480] diff --git a/src/main/webapp/javascript/interface.js b/src/main/webapp/javascript/interface.js index 74009b5..466ab2f 100644 --- a/src/main/webapp/javascript/interface.js +++ b/src/main/webapp/javascript/interface.js @@ -451,6 +451,8 @@ $.SvgCanvas = function(container, config, style) { var width = config.dimensions[0]; var height = config.dimensions[1]; + + var started = false; var locked = false; @@ -1945,9 +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, - right: 0, - states: [], + left: 0, //for 'prodaut' + right: 0, //for 'prodaut' + states: [], //for 'powaut' initial: false, accepting: false, reflexiveNum: reflNum, @@ -2206,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; @@ -2330,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"; diff --git a/src/main/webapp/minimization/applet-solve.html b/src/main/webapp/minimization/applet-solve.html index ff81fa7..93e4fdf 100644 --- a/src/main/webapp/minimization/applet-solve.html +++ b/src/main/webapp/minimization/applet-solve.html @@ -1,39 +1 @@ -
- - - - - - - - - - - - - - -
- -
-
-
-
- -
- - - -
+ diff --git a/src/main/webapp/minimization/solve.html b/src/main/webapp/minimization/solve.html index 9911b11..f911335 100644 --- a/src/main/webapp/minimization/solve.html +++ b/src/main/webapp/minimization/solve.html @@ -1,4 +1,17 @@
+ +

Solve minimization problem

Construct an automaton that recognizes the following language of strings over the alphabet @@ -7,7 +20,52 @@

Solve minimization problem


- + + +
+ + + + + + + + + + + + + + +
+ +
+
+
+ +

+ + +
+
+ +
+ + + +
diff --git a/src/main/webapp/product-construction/applet-create-edit.html b/src/main/webapp/product-construction/applet-create-edit.html index 6331ae7..7c6ce2a 100644 --- a/src/main/webapp/product-construction/applet-create-edit.html +++ b/src/main/webapp/product-construction/applet-create-edit.html @@ -16,8 +16,8 @@
-
-
+
+
diff --git a/src/main/webapp/product-construction/applet-solve-product.html b/src/main/webapp/product-construction/applet-solve-product.html index 6a7cec0..b54d880 100644 --- a/src/main/webapp/product-construction/applet-solve-product.html +++ b/src/main/webapp/product-construction/applet-solve-product.html @@ -16,8 +16,10 @@
-
-
+
+ + +
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; From 9d45647316bad1916d92dc736e7115f2fff3ac2f Mon Sep 17 00:00:00 2001 From: Jan Wagener Date: Tue, 29 Aug 2017 07:02:53 +0200 Subject: [PATCH 20/25] Minimization Updates --- .../automatatutor/snippet/MinimizationSnippet.scala | 9 ++++----- .../webapp/javascript/includeMinimizationSolve.js | 2 +- src/main/webapp/minimization/solve.html | 12 ++++++++---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala index ee58ab1..23e8512 100644 --- a/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/MinimizationSnippet.scala @@ -107,7 +107,7 @@ object MinimizationSnippet extends ProblemSnippet { def grade(minimizationTableDescription : String /*, attemptDfaDescription : String*/ ) : JsCmd = { //Temporary: - val attemptDfaDescription = "" + val attemptDfaDescription = minimizationProblem.getXmlDescription.toString if(remainingAttempts() <= 0) { return JsShowId("feedbackdisplay") & @@ -117,10 +117,8 @@ object MinimizationSnippet extends ProblemSnippet { maxGrade.toString + ".")) } - val attemptDfaXml = XML.loadString(attemptDfaDescription) val correctDfaDescription = minimizationProblem.getXmlDescription.toString val attemptTime = Calendar.getInstance.getTime() - val x = List(correctDfaDescription, correctDfaDescription) //TODO: Implement getMinimizationFeedback() val graderResponse = GraderConnection.getMinimizationFeedback(correctDfaDescription, minimizationTableDescription, attemptDfaDescription, maxGrade.toInt) @@ -142,7 +140,7 @@ object MinimizationSnippet extends ProblemSnippet { val problemAlphabet = minimizationProblem.getAlphabet val automaton = XML.loadString(minimizationProblem.getAutomaton) - val n = ((automaton \ "stateSet") \ "_").length + 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) @@ -154,7 +152,7 @@ object MinimizationSnippet extends ProblemSnippet { {row.map(col => { - SHtml.text("", value => {}, "class" -> "", "start" -> col._1.toString(), "end" -> col._2.toString(), "size" -> "5") + 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() + ")") } @@ -166,6 +164,7 @@ object MinimizationSnippet extends ProblemSnippet { val problemAlphabetNodeSeq = Text("{" + problemAlphabet.mkString(",") + "}") diff --git a/src/main/webapp/javascript/includeMinimizationSolve.js b/src/main/webapp/javascript/includeMinimizationSolve.js index dea2fc1..ac13106 100644 --- a/src/main/webapp/javascript/includeMinimizationSolve.js +++ b/src/main/webapp/javascript/includeMinimizationSolve.js @@ -12,7 +12,7 @@ function initCanvas() { Editor.canvasDfaIn = new $.SvgCanvas("#svgcanvasdfain", Editor.curConfigDfaIn, 'detaut'); } if(!Editor.canvasDfaSol) { - Editor.canvasDfaSol = new $.SvgCanvas("#svgcanvasdfasol", Editor.curConfigDfaSol, 'detaut'); + Editor.canvasDfaSol = new $.SvgCanvas("#svgcanvasdfasol", Editor.curConfigDfaSol, 'powaut'); } } diff --git a/src/main/webapp/minimization/solve.html b/src/main/webapp/minimization/solve.html index f911335..04e148e 100644 --- a/src/main/webapp/minimization/solve.html +++ b/src/main/webapp/minimization/solve.html @@ -1,13 +1,17 @@
From 316016381881ffe3c144875d57768d1d38a0f3d3 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Thu, 7 Sep 2017 20:09:00 +0200 Subject: [PATCH 21/25] minor UI changes for context free grammar problems --- src/main/webapp/cyk-problem/create.html | 4 ++-- src/main/webapp/cyk-problem/edit.html | 4 ++-- src/main/webapp/cyk-problem/solve.html | 6 +++--- .../webapp/description-to-grammar-problem/create.html | 4 ++-- src/main/webapp/description-to-grammar-problem/edit.html | 4 ++-- src/main/webapp/description-to-grammar-problem/solve.html | 4 ++-- src/main/webapp/grammar-to-cnf-problem/create.html | 4 ++-- src/main/webapp/grammar-to-cnf-problem/edit.html | 4 ++-- src/main/webapp/grammar-to-cnf-problem/solve.html | 8 ++++---- src/main/webapp/words-in-grammar-problem/create.html | 8 ++++---- src/main/webapp/words-in-grammar-problem/edit.html | 8 ++++---- src/main/webapp/words-in-grammar-problem/solve.html | 4 ++-- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/main/webapp/cyk-problem/create.html b/src/main/webapp/cyk-problem/create.html index 1d5b0ed..8eff8f6 100644 --- a/src/main/webapp/cyk-problem/create.html +++ b/src/main/webapp/cyk-problem/create.html @@ -1,12 +1,12 @@
-

Create a new CYK Problem

+

Create a new CYK problem

Grammar Syntax

  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • 1 line per production
  • first variable is start variable
  • -
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
diff --git a/src/main/webapp/cyk-problem/edit.html b/src/main/webapp/cyk-problem/edit.html index 64db40d..6fda1af 100644 --- a/src/main/webapp/cyk-problem/edit.html +++ b/src/main/webapp/cyk-problem/edit.html @@ -1,12 +1,12 @@
-

Edit a CYK Problem

+

Edit a CYK problem

Grammar Syntax

  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • 1 line per production
  • first variable is start variable
  • -
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
diff --git a/src/main/webapp/cyk-problem/solve.html b/src/main/webapp/cyk-problem/solve.html index 1a01093..91396ab 100644 --- a/src/main/webapp/cyk-problem/solve.html +++ b/src/main/webapp/cyk-problem/solve.html @@ -18,17 +18,17 @@

Grammar Syntax

  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • 1 line per production
  • first variable is start variable
  • -
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
  • -

    The Problem

    +

    Problem

    For the grammar with the productions:


    - Perform the CYK Algorithm! + 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 index 835cf4b..e152a81 100644 --- a/src/main/webapp/description-to-grammar-problem/create.html +++ b/src/main/webapp/description-to-grammar-problem/create.html @@ -1,12 +1,12 @@
    -

    Create a new Description to Grammar Problem

    +

    Create a new Description to Grammar problem

    Grammar Syntax

    • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
    • 1 line per production
    • first variable is start variable
    • -
    • possible terminals: lower case characters a-z or '(' ')' '[' ']'
    • +
    • 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 | ")
    diff --git a/src/main/webapp/description-to-grammar-problem/edit.html b/src/main/webapp/description-to-grammar-problem/edit.html index 22fa8c4..d46c734 100644 --- a/src/main/webapp/description-to-grammar-problem/edit.html +++ b/src/main/webapp/description-to-grammar-problem/edit.html @@ -2,14 +2,14 @@ -

    Edit a Description to Grammar Problem

    +

    Edit a Description to Grammar problem

    Grammar Syntax

    • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
    • 1 line per production
    • first variable is start variable
    • -
    • possible terminals: lower case characters a-z or '(' ')' '[' ']'
    • +
    • 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 | ")
    diff --git a/src/main/webapp/description-to-grammar-problem/solve.html b/src/main/webapp/description-to-grammar-problem/solve.html index 0255800..2e8c11e 100644 --- a/src/main/webapp/description-to-grammar-problem/solve.html +++ b/src/main/webapp/description-to-grammar-problem/solve.html @@ -6,12 +6,12 @@

    Grammar Syntax

  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • 1 line per production
  • first variable is start variable
  • -
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
  • -

    The Problem

    +

    Problem

    Find a grammar that recognizes the following language:



    diff --git a/src/main/webapp/grammar-to-cnf-problem/create.html b/src/main/webapp/grammar-to-cnf-problem/create.html index 699726c..f227611 100644 --- a/src/main/webapp/grammar-to-cnf-problem/create.html +++ b/src/main/webapp/grammar-to-cnf-problem/create.html @@ -1,12 +1,12 @@
    -

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

    +

    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
    • first variable is start variable
    • -
    • possible terminals: lower case characters a-z or '(' ')' '[' ']'
    • +
    • 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 | ")
    diff --git a/src/main/webapp/grammar-to-cnf-problem/edit.html b/src/main/webapp/grammar-to-cnf-problem/edit.html index d0849cf..0ac6263 100644 --- a/src/main/webapp/grammar-to-cnf-problem/edit.html +++ b/src/main/webapp/grammar-to-cnf-problem/edit.html @@ -1,12 +1,12 @@
    -

    Edit a Grammar to CNF (Comsky Normal Form) Problem

    +

    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
    • first variable is start variable
    • -
    • possible terminals: lower case characters a-z or '(' ')' '[' ']'
    • +
    • 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 | ")
    diff --git a/src/main/webapp/grammar-to-cnf-problem/solve.html b/src/main/webapp/grammar-to-cnf-problem/solve.html index 2b2e432..dbf7abc 100644 --- a/src/main/webapp/grammar-to-cnf-problem/solve.html +++ b/src/main/webapp/grammar-to-cnf-problem/solve.html @@ -1,23 +1,23 @@
    -

    Solve Grammar to CNF (Comsky Normal Form) Problem

    +

    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
    • first variable is start variable
    • -
    • possible terminals: lower case characters a-z or '(' ')' '[' ']'
    • +
    • 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 | ")
    -

    The Problem

    +

    Problem

    For the grammar G with the productions:


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

    diff --git a/src/main/webapp/words-in-grammar-problem/create.html b/src/main/webapp/words-in-grammar-problem/create.html index a9dbd53..6f32d76 100644 --- a/src/main/webapp/words-in-grammar-problem/create.html +++ b/src/main/webapp/words-in-grammar-problem/create.html @@ -1,12 +1,12 @@
    -

    Create a new Words in Grammar Problem

    +

    Create a new Words in Grammar problem

    Grammar Syntax

    • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
    • 1 line per production
    • first variable is start variable
    • -
    • possible terminals: lower case characters a-z or '(' ')' '[' ']'
    • +
    • 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 | ")
    @@ -16,11 +16,11 @@

    Problem Definition

    - + - + diff --git a/src/main/webapp/words-in-grammar-problem/edit.html b/src/main/webapp/words-in-grammar-problem/edit.html index f6be6be..86825d7 100644 --- a/src/main/webapp/words-in-grammar-problem/edit.html +++ b/src/main/webapp/words-in-grammar-problem/edit.html @@ -1,12 +1,12 @@
    -

    Edit a Words in Grammar Problem

    +

    Edit a Words in Grammar problem

    Grammar Syntax

    • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
    • 1 line per production
    • first variable is start variable
    • -
    • possible terminals: lower case characters a-z or '(' ')' '[' ']'
    • +
    • 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 | ")
    @@ -16,11 +16,11 @@

    Problem Definition

    Number of Words IN Grammar: Number of words IN grammar:
    Number of Words NOT IN Grammar: Number of words NOT IN grammar:
    - + - + diff --git a/src/main/webapp/words-in-grammar-problem/solve.html b/src/main/webapp/words-in-grammar-problem/solve.html index 3a96f21..3ea6f26 100644 --- a/src/main/webapp/words-in-grammar-problem/solve.html +++ b/src/main/webapp/words-in-grammar-problem/solve.html @@ -8,12 +8,12 @@

    Grammar Syntax

  • e.g. S -> X Y | TEIL1 a TEIL2 | S S | | a
  • 1 line per production
  • first variable is start variable
  • -
  • possible terminals: lower case characters a-z or '(' ')' '[' ']'
  • +
  • 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 | ")
  • -

    The Problem

    +

    Problem

    For the grammar with the productions:


    From 5b4a28bc2dc686a2774b24186d661ff91a66ca8b Mon Sep 17 00:00:00 2001 From: ga63nij Date: Sun, 10 Sep 2017 14:27:29 +0200 Subject: [PATCH 22/25] Bugfix in Courses --- .../com/automatatutor/snippet/Courses.scala | 26 ------------------- src/main/webapp/courses/index.html | 3 ++- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/main/scala/com/automatatutor/snippet/Courses.scala b/src/main/scala/com/automatatutor/snippet/Courses.scala index 08c19d4..a91ee30 100644 --- a/src/main/scala/com/automatatutor/snippet/Courses.scala +++ b/src/main/scala/com/automatatutor/snippet/Courses.scala @@ -68,32 +68,6 @@ class Courses { return attendedCoursesNodeSeq ++ supervisedCoursesNodeSeq ++ createCourseLink } - def showall2(ignored : NodeSeq) : NodeSeq = { - - val attendedCourses = User.currentUser.map(_.getAttendedCourses) openOr List() - val attendedCoursesNodeSeq = if (!attendedCourses.isEmpty) { -

    Attended Courses

    ++ { displayAttendedCourses(attendedCourses) } - } else { -

    Där är inga kurser!

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

    Supervised Courses

    ++ { displaySupervisedCourses(supervisedCourses) } - } 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 - } - def renderSolvePracticeSet( practiceSet : ProblemSet ) : NodeSeq = { val posedProblems = practiceSet.getPosedProblems diff --git a/src/main/webapp/courses/index.html b/src/main/webapp/courses/index.html index 5ab6f51..057e2f4 100644 --- a/src/main/webapp/courses/index.html +++ b/src/main/webapp/courses/index.html @@ -4,7 +4,8 @@
    - + +

    Enroll in course

    From 5b0163ffeeb088d2166dce6d55b0151f6eba3c9d Mon Sep 17 00:00:00 2001 From: ga63nij Date: Thu, 14 Sep 2017 00:32:32 +0200 Subject: [PATCH 23/25] edited grammar syntax text --- src/main/webapp/cyk-problem/create.html | 1 + src/main/webapp/cyk-problem/edit.html | 1 + src/main/webapp/cyk-problem/solve.html | 1 + src/main/webapp/description-to-grammar-problem/create.html | 1 + src/main/webapp/description-to-grammar-problem/edit.html | 1 + src/main/webapp/description-to-grammar-problem/solve.html | 1 + src/main/webapp/grammar-to-cnf-problem/create.html | 1 + src/main/webapp/grammar-to-cnf-problem/edit.html | 1 + src/main/webapp/grammar-to-cnf-problem/solve.html | 1 + src/main/webapp/words-in-grammar-problem/create.html | 1 + src/main/webapp/words-in-grammar-problem/edit.html | 1 + src/main/webapp/words-in-grammar-problem/solve.html | 1 + 12 files changed, 12 insertions(+) diff --git a/src/main/webapp/cyk-problem/create.html b/src/main/webapp/cyk-problem/create.html index 8eff8f6..dc9a08b 100644 --- a/src/main/webapp/cyk-problem/create.html +++ b/src/main/webapp/cyk-problem/create.html @@ -5,6 +5,7 @@

    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!)
    • diff --git a/src/main/webapp/cyk-problem/edit.html b/src/main/webapp/cyk-problem/edit.html index 6fda1af..36d1394 100644 --- a/src/main/webapp/cyk-problem/edit.html +++ b/src/main/webapp/cyk-problem/edit.html @@ -5,6 +5,7 @@

      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!)
      • diff --git a/src/main/webapp/cyk-problem/solve.html b/src/main/webapp/cyk-problem/solve.html index 91396ab..05d3b6a 100644 --- a/src/main/webapp/cyk-problem/solve.html +++ b/src/main/webapp/cyk-problem/solve.html @@ -17,6 +17,7 @@

        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!)
        • diff --git a/src/main/webapp/description-to-grammar-problem/create.html b/src/main/webapp/description-to-grammar-problem/create.html index e152a81..545e71f 100644 --- a/src/main/webapp/description-to-grammar-problem/create.html +++ b/src/main/webapp/description-to-grammar-problem/create.html @@ -5,6 +5,7 @@

          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!)
          • diff --git a/src/main/webapp/description-to-grammar-problem/edit.html b/src/main/webapp/description-to-grammar-problem/edit.html index d46c734..47ce597 100644 --- a/src/main/webapp/description-to-grammar-problem/edit.html +++ b/src/main/webapp/description-to-grammar-problem/edit.html @@ -8,6 +8,7 @@

            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!)
            • diff --git a/src/main/webapp/description-to-grammar-problem/solve.html b/src/main/webapp/description-to-grammar-problem/solve.html index 2e8c11e..a73b9ed 100644 --- a/src/main/webapp/description-to-grammar-problem/solve.html +++ b/src/main/webapp/description-to-grammar-problem/solve.html @@ -5,6 +5,7 @@

              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!)
              • diff --git a/src/main/webapp/grammar-to-cnf-problem/create.html b/src/main/webapp/grammar-to-cnf-problem/create.html index f227611..4469931 100644 --- a/src/main/webapp/grammar-to-cnf-problem/create.html +++ b/src/main/webapp/grammar-to-cnf-problem/create.html @@ -5,6 +5,7 @@

                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!)
                • diff --git a/src/main/webapp/grammar-to-cnf-problem/edit.html b/src/main/webapp/grammar-to-cnf-problem/edit.html index 0ac6263..e3731bf 100644 --- a/src/main/webapp/grammar-to-cnf-problem/edit.html +++ b/src/main/webapp/grammar-to-cnf-problem/edit.html @@ -5,6 +5,7 @@

                  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!)
                  • diff --git a/src/main/webapp/grammar-to-cnf-problem/solve.html b/src/main/webapp/grammar-to-cnf-problem/solve.html index dbf7abc..331e276 100644 --- a/src/main/webapp/grammar-to-cnf-problem/solve.html +++ b/src/main/webapp/grammar-to-cnf-problem/solve.html @@ -5,6 +5,7 @@

                    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!)
                    • diff --git a/src/main/webapp/words-in-grammar-problem/create.html b/src/main/webapp/words-in-grammar-problem/create.html index 6f32d76..32991d2 100644 --- a/src/main/webapp/words-in-grammar-problem/create.html +++ b/src/main/webapp/words-in-grammar-problem/create.html @@ -5,6 +5,7 @@

                      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!)
                      • diff --git a/src/main/webapp/words-in-grammar-problem/edit.html b/src/main/webapp/words-in-grammar-problem/edit.html index 86825d7..a1d2ac0 100644 --- a/src/main/webapp/words-in-grammar-problem/edit.html +++ b/src/main/webapp/words-in-grammar-problem/edit.html @@ -5,6 +5,7 @@

                        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!)
                        • diff --git a/src/main/webapp/words-in-grammar-problem/solve.html b/src/main/webapp/words-in-grammar-problem/solve.html index 3ea6f26..bec95fd 100644 --- a/src/main/webapp/words-in-grammar-problem/solve.html +++ b/src/main/webapp/words-in-grammar-problem/solve.html @@ -7,6 +7,7 @@

                          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!)
                          • From ff35719e0fccf73114e939127bd91bbcf29821dd Mon Sep 17 00:00:00 2001 From: ga63nij Date: Fri, 20 Oct 2017 18:30:29 +0200 Subject: [PATCH 24/25] Minor Bugfix WordsInGrammar instruction grammar mistake (singular vs plural) --- .../com/automatatutor/snippet/WordsInGrammarSnippet.scala | 6 ++++-- src/main/webapp/words-in-grammar-problem/solve.html | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala index 5c0484a..0f91e15 100644 --- a/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala +++ b/src/main/scala/com/automatatutor/snippet/WordsInGrammarSnippet.scala @@ -183,8 +183,10 @@ object WordsInGrammarSnippet extends ProblemSnippet { val problemDescription = generalProblem.getLongDescription val grammarText = { specificProblem.getGrammar.replaceAll("->", " -> ").replaceAll("=>", " -> ").replaceAll("\\|", " \\| ").replaceAll("\\s{2,}", " ").split("\\s(?=\\S+\\s*->)").map {Text(_) ++
                            } reduceLeft (_ ++ _) } - val inNeededText = Text("" + specificProblem.inNeeded) - val outNeededText = Text("" + specificProblem.outNeeded) + 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") diff --git a/src/main/webapp/words-in-grammar-problem/solve.html b/src/main/webapp/words-in-grammar-problem/solve.html index bec95fd..5495767 100644 --- a/src/main/webapp/words-in-grammar-problem/solve.html +++ b/src/main/webapp/words-in-grammar-problem/solve.html @@ -19,7 +19,7 @@

                            Problem

                            For the grammar with the productions:


                            - Give words that are recognized and words that aren't recognized! + Give that the grammar recognizes and that the grammar doesn't recognize!

                            From 835c6d7ad14592770a312a226d6a543096ac25b7 Mon Sep 17 00:00:00 2001 From: ga63nij Date: Sat, 21 Oct 2017 14:13:36 +0200 Subject: [PATCH 25/25] CYK: new alert message after invalid user input --- src/main/webapp/cyk-problem/solve.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/webapp/cyk-problem/solve.html b/src/main/webapp/cyk-problem/solve.html index 05d3b6a..6e94fb4 100644 --- a/src/main/webapp/cyk-problem/solve.html +++ b/src/main/webapp/cyk-problem/solve.html @@ -3,11 +3,17 @@ function buildCYKTableXML() { var res = ''; var inputs = document.getElementsByClassName('cyk'); + var changedSomething = false; for (var i = 0; i < inputs.length; ++i) { + var before = inputs[i].value; inputs[i].value = inputs[i].value.replace(/[^A-Z\s]/g, ""); + if (inputs[i].value !== before) changedSomething = true; res += '' + inputs[i].value + ''; } res += ''; + + if (changedSomething) alert("Please make sure to only use nonterminals (devided by shitespaces) in the table!"); + return res; }
    Number of Words IN Grammar: Number of words IN grammar:
    Number of Words NOT IN Grammar: Number of words NOT IN grammar: