Skip to content

Support for Context Free Grammars and 2 Exercise Types for DFAs/NFAs #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1da518e
UI changes (quality of life):
May 21, 2017
3a7ec4d
Gerüst für Produktkonstruktion
JanWagener May 25, 2017
df4c7b2
Gerüst für Produktkonstruktion funktionierend
JanWagener Jun 11, 2017
c7851e9
new exercise types ("Description to Grammar" and "Grammar To CNF")
Jul 6, 2017
e0d687b
new exercise type: CYK Algorithm
Jul 10, 2017
675a42c
updated gitignore
Jul 10, 2017
ae9f641
removed input "long_description" in multiple exercises that dont use it
Jul 18, 2017
c53f7b3
Update SOAPConnection.scala to revert hotfix
Jul 22, 2017
85bd1c3
Merge branch 'martin_local' into 'master'
Jul 22, 2017
ce906f0
Auch alles!
JanWagener Jul 23, 2017
e040b2a
MinimizationProblem.scala
JanWagener Jul 23, 2017
b3b9e1c
MinimizationSnippet.scala
JanWagener Jul 23, 2017
258ca58
minimization folder
JanWagener Jul 23, 2017
cd9c664
More unversioned files
JanWagener Jul 23, 2017
f174745
Merge branch 'master' into jan
JanWagener Jul 23, 2017
4ead4de
imports cleaned up
JanWagener Jul 23, 2017
2c0d6d6
return link in practice problems fixed
Jul 24, 2017
25c215d
states of product automata named as tuples
JanWagener Jul 25, 2017
07f2796
Merge branch 'master' of https://gitlab.lrz.de/i7/automatatutor/autom…
JanWagener Jul 25, 2017
842370b
minor bugfixes (negativ grades when parsing error, sanitizeInput edited)
Aug 14, 2017
03eef77
WordsInGrammar: limit max. word length to prevent server overload
Aug 14, 2017
259d260
Product Construction, Minimization - Layout updates
JanWagener Aug 28, 2017
4ec95c3
Merge remote-tracking branch 'origin/master'
JanWagener Aug 28, 2017
9d45647
Minimization Updates
JanWagener Aug 29, 2017
3160163
minor UI changes for context free grammar problems
Sep 7, 2017
5b4a28b
Bugfix in Courses
Sep 10, 2017
5b0163f
edited grammar syntax text
Sep 13, 2017
ff35719
Minor Bugfix
Oct 20, 2017
835c6d7
CYK: new alert message after invalid user input
Oct 21, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
.settings/
console.devmode.log
develop.db.mv.db
develop.db.trace.db
project/project/
project/target/
target/
src/main/resources/props/default.props
src/main/resources/props/production.default.props
.idea
/.gitignore
8 changes: 7 additions & 1 deletion src/main/scala/bootstrap/liftweb/Boot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ class Boot {
Schemifier.schemify(true, Schemifier.infoF _,
User, Attendance, Course, PosedProblem, PosedProblemSet, Problem, ProblemType,
DFAConstructionProblem, DFAConstructionSolutionAttempt,
ProductConstructionProblem, ProductConstructionSolutionAttempt,
MinimizationProblem, MinimizationSolutionAttempt,
NFAConstructionProblem, NFAConstructionSolutionAttempt,
NFAToDFAProblem, NFAToDFASolutionAttempt,
RegExConstructionProblem, RegexConstructionSolutionAttempt,
PumpingLemmaProblem, PumpingLemmaSolutionAttempt,
WordsInGrammarProblem, WordsInGrammarSolutionAttempt,
GrammarToCNFProblem, GrammarToCNFSolutionAttempt,
DescriptionToGrammarProblem, DescriptionToGrammarSolutionAttempt,
CYKProblem, CYKSolutionAttempt,
ProblemSet, Role, SolutionAttempt, Supervision)

StartupHooks.hooks map (hook => hook())
Expand All @@ -62,7 +68,7 @@ class Boot {

Menu.i("Practice Problem Sets") / "practicesets" / "index" >> loggedInPredicate submenus(
Menu.i("Solve Practice Set Problem") /"practicesets" / "solve" >> Hidden),

Menu.i("Problems") / "problems" / "index" >> isInstructorPredicate submenus(
Menu.i("Create Problem") / "problems" / "create" >> Hidden,
Menu.i("Edit Problem") / "problems" / "edit" >> Hidden),
Expand Down
119 changes: 119 additions & 0 deletions src/main/scala/com/automatatutor/lib/SOAPConnection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,47 @@ object GraderConnection {

return ((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))
}

//Product Construction

def getProductConstructionFeedback(correctDfaDescriptionList : List[String], attemptDfaDescription : String, booleanOperation : String, maxGrade : Int) : (Int, NodeSeq) = {

def stringListToNodeList(xs: List[String]): List[Node] = xs match{
case Nil => List()
case y :: ys => Elem(null, "dfaDesc", Null, TopScope, true, XML.loadString(y)) :: stringListToNodeList(ys)
}

val arguments = Map[String, Node](
"dfaDescList" -> Elem(null, "dfaDescList", Null, TopScope, true, stringListToNodeList(correctDfaDescriptionList):_*),
"dfaAttemptDesc" -> XML.loadString(attemptDfaDescription),
"booleanOperation" -> Elem(null, "booleanOperation", Null, TopScope, true, Text(booleanOperation)),
"maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
"feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")),
"enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored")))

val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackProductConstruction", arguments)

((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))
}

// Minimization

//TODO: Pass Minimization Table to variable 'minimizationTableAttempt'
def getMinimizationFeedback(dfaDescription : String, minimizationTableDescription : String, attemptDfaDescription : String, maxGrade : Int) : (Int, NodeSeq) = {

val arguments = Map[String, Node](
"dfaDesc" -> XML.loadString(dfaDescription),
"minimizationTableAttempt" -> XML.loadString(minimizationTableDescription),
"dfaAttemptDesc" -> XML.loadString(attemptDfaDescription),
"maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
"feedbackLevel" -> Elem(null, "feedbackLevel", Null, TopScope, true, Text("Hint")),
"enabledFeedbacks" -> Elem(null, "enabledFeedbacks", Null, TopScope, true, Text("ignored")));

//TODO: Implement 'ComputeFeedbackMinimization' in Backend
val responseXml = soapConnection.callMethod(namespace, "ComputeFeedbackMinimization", arguments)

((responseXml \ "grade").text.toInt, (responseXml \ "feedString" \ "ul" \ "li"))
}

// NFA

Expand Down Expand Up @@ -175,6 +216,84 @@ object GraderConnection {
if(responseXml.text.equals("CorrectRegex")) return List() else return List(responseXml.text)
}

//Grammar
def getGrammarParsingErrors(potentialGrammar : String) : Seq[String] = {
val arguments = Map[String, Node](
"grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(potentialGrammar))
)

val responseXml = soapConnection.callMethod(namespace, "CheckGrammar", arguments)

if(responseXml.text.equals("CorrectGrammar")) return List() else return List(responseXml.text)
}

def getCNFParsingErrors(potentialGrammar : String) : Seq[String] = {
val arguments = Map[String, Node](
"grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(potentialGrammar))
)

val responseXml = soapConnection.callMethod(namespace, "isCNF", arguments)

if((responseXml \ "res").head.text.equals("y")) return List() else return List((responseXml \ "feedback").head.text)
}

def getWordsInGrammarFeedback(grammar: String, wordsIn : Seq[String], wordsOut : Seq[String], maxGrade : Int) : (Int, NodeSeq) = {
val arguments = Map[String, Node](
"grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(grammar)),
"wordsIn" -> <div> { wordsIn.map((symbol : String) => Elem(null, "word", Null, TopScope, true, Text(symbol))) } </div>,
"wordsOut" -> <div> { wordsOut.map((symbol : String) => Elem(null, "word", Null, TopScope, true, Text(symbol))) } </div>,
"maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString))
);

val responseXml = soapConnection.callMethod(namespace, "ComputeWordsInGrammarFeedback", arguments)

return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback"))
}

def getDescriptionToGrammarFeedback(solution: String, attempt: String, maxGrade : Int) : (Int, NodeSeq) = {
val arguments = Map[String, Node](
"solution" -> Elem(null, "solution", Null, TopScope, true, Text(solution)),
"attempt" -> Elem(null, "attempt", Null, TopScope, true, Text(attempt)),
"maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
"checkEmptyWord" -> Elem(null, "checkEmptyWord", Null, TopScope, true, Text(true.toString))
);

val responseXml = soapConnection.callMethod(namespace, "ComputeGrammarEqualityFeedback", arguments)

return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback"))
}

def getGrammarToCNFFeedback(solution: String, attempt: String, maxGrade : Int) : (Int, NodeSeq) = {
//check that attempt is in CNF
val arguments1 = Map[String, Node](
"grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(attempt))
);
val responseXml1 = soapConnection.callMethod(namespace, "isCNF", arguments1)
if ((responseXml1 \ "res").head.text == "n") return (-1, (responseXml1 \ "feedback"))

val arguments2 = Map[String, Node](
"solution" -> Elem(null, "solution", Null, TopScope, true, Text(solution)),
"attempt" -> Elem(null, "attempt", Null, TopScope, true, Text(attempt)),
"maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString)),
"checkEmptyWord" -> Elem(null, "checkEmptyWord", Null, TopScope, true, Text(false.toString))
);
val responseXml2 = soapConnection.callMethod(namespace, "ComputeGrammarEqualityFeedback", arguments2)
return ((responseXml2 \ "grade").head.text.toInt, (responseXml2 \ "feedback"))
}

def getCYKFeedback(grammar: String, word: String, cyk_attempt: String, maxGrade : Int) : (Int, NodeSeq) = {
val arguments = Map[String, Node](
"grammar" -> Elem(null, "grammar", Null, TopScope, true, Text(grammar)),
"word" -> Elem(null, "word", Null, TopScope, true, Text(word)),
"attempt" -> XML.loadString(cyk_attempt),
"maxGrade" -> Elem(null, "maxGrade", Null, TopScope, true, Text(maxGrade.toString))
);

val responseXml = soapConnection.callMethod(namespace, "ComputeCYKFeedback", arguments)

return ((responseXml \ "grade").head.text.toInt, (responseXml \ "feedback"))
}

// Pumping lemma

def getPLParsingErrors(languageDesc : String, constraintDesc : String,
Expand Down
45 changes: 45 additions & 0 deletions src/main/scala/com/automatatutor/model/CYKProblem.scala
Original file line number Diff line number Diff line change
@@ -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))
}
Original file line number Diff line number Diff line change
@@ -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))
}
41 changes: 41 additions & 0 deletions src/main/scala/com/automatatutor/model/GrammarToCNFProblem.scala
Original file line number Diff line number Diff line change
@@ -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))
}
49 changes: 49 additions & 0 deletions src/main/scala/com/automatatutor/model/MinimizationProblem.scala
Original file line number Diff line number Diff line change
@@ -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))
}
Loading