Commit 8f349041 authored by Björn Eyselein's avatar Björn Eyselein
Browse files

Zwischencommit

parent b3a2d689
package controllers
import java.time.LocalDate
import java.time.temporal.ChronoUnit
import javax.inject.{Inject, Singleton}
import model._
import play.api.mvc._
......@@ -78,8 +81,18 @@ class HomeController @Inject()(cc: ControllerComponents, protected val tableDefs
}
def learn(langId: Int, collId: Int, cardId: Int, isRepeating: Boolean): EssentialAction =
withUserAndCompleteFlashcard(adminRightsRequired = false, langId, collId, cardId) { (user, _, _, completeFlashcard) =>
implicit request => Ok(views.html.learn(user, completeFlashcard, isRepeating))
futureWithUserAndCompleteFlashcard(adminRightsRequired = false, langId, collId, cardId) { (user, _, _, completeFlashcard) =>
implicit request =>
val futureMaybeOldAnswer: Future[Option[UserAnsweredFlashcard]] = if (isRepeating) {
tableDefs.futureUserAnswerForFlashcard(user, completeFlashcard.flashcard)
} else Future.successful(None)
futureMaybeOldAnswer map { maybeOldAnswer =>
// val oldAnswerIsActive = maybeOldAnswer.exists(_.isActive)
// println(oldAnswerIsActive)
Ok(views.html.learn(user, completeFlashcard, maybeOldAnswer, isRepeating))
}
}
def checkSolution(langId: Int, collId: Int, cardId: Int): EssentialAction =
......@@ -88,7 +101,7 @@ class HomeController @Inject()(cc: ControllerComponents, protected val tableDefs
request.body.asJson flatMap (json => JsonFormats.solutionFormat.reads(json).asOpt) match {
case None => Future(BadRequest("Could not read solution..."))
case Some(solution) =>
val correctionResult = Corrector.correct(completeFlashcard, solution)
val correctionResult: CorrectionResult = Corrector.correct(completeFlashcard, solution)
tableDefs.futureInsertOrUpdateUserAnswer(user, completeFlashcard.flashcard, correctionResult.correct) map {
_ => Ok(JsonFormats.correctionResultWrites.writes(correctionResult))
......
......@@ -16,22 +16,28 @@ object Corrector {
missing = correctIds diff selectedIds
)
def correctTextualFlashcard(flashcard: Flashcard, solution: Solution): CorrectionResult = flashcard.meaning match {
case None => ???
case Some(meaning) =>
val editOperations: Seq[EditOperation] = Levenshtein.calculateBacktrace(solution.solution, meaning)
CorrectionResult(editOperations.isEmpty, flashcard.cardType, solution, editOperations, None)
}
def correctChoiceFlashcard(flashcard: Flashcard, answers: Seq[ChoiceAnswer], solution: Solution): CorrectionResult = {
val selectedAnswerIds: Seq[Int] = solution.selectedAnswers
val correctAnswerIds: Seq[Int] = answers.filter(_.correctness != Correctness.Wrong).map(_.id)
val answerSelectionResult = matchAnswerIds(selectedAnswerIds, correctAnswerIds)
CorrectionResult(answerSelectionResult.isCorrect, flashcard.cardType, solution, Seq[EditOperation](), Some(answerSelectionResult))
}
def correct(completeFlashcard: CompleteFlashcard, solution: Solution): CorrectionResult = completeFlashcard.flashcard.cardType match {
case CardType.Vocable | CardType.Text =>
completeFlashcard.flashcard.meaning match {
case None => ???
case Some(meaning) =>
val editOperations = Levenshtein.calculateBacktrace(solution.solution, meaning)
CorrectionResult(editOperations.isEmpty, completeFlashcard.flashcard.cardType, solution, editOperations, None)
}
case CardType.SingleChoice | CardType.MultipleChoice =>
val selectedAnswerIds = solution.selectedAnswers
val correctAnswerIds = completeFlashcard.choiceAnswers.filter(_.correctness != Correctness.Wrong).map(_.id)
val answerSelectionResult = matchAnswerIds(selectedAnswerIds, correctAnswerIds)
CorrectionResult(answerSelectionResult.isCorrect, completeFlashcard.flashcard.cardType, solution, Seq[EditOperation](), Some(answerSelectionResult))
case CardType.Vocable | CardType.Text => correctTextualFlashcard(completeFlashcard.flashcard, solution)
case CardType.SingleChoice | CardType.MultipleChoice => correctChoiceFlashcard(completeFlashcard.flashcard, completeFlashcard.choiceAnswers, solution)
}
}
\ No newline at end of file
package model
import java.time.LocalDate
import java.time.temporal.ChronoUnit
import enumeratum.{EnumEntry, PlayEnum}
......@@ -74,4 +75,8 @@ final case class CompleteFlashcard(flashcard: Flashcard, choiceAnswers: Seq[Choi
// User answered flashcard
final case class UserAnsweredFlashcard(username: String, cardId: Int, collId: Int, langId: Int, bucketId: Int, dateAnswered: LocalDate, correct: Boolean, tries: Int)
\ No newline at end of file
final case class UserAnsweredFlashcard(username: String, cardId: Int, collId: Int, langId: Int, bucketId: Int, dateAnswered: LocalDate, correct: Boolean, tries: Int) {
def isActive: Boolean = dateAnswered.until(LocalDate.now(), ChronoUnit.DAYS) < Math.pow(3, bucketId - 1)
}
\ No newline at end of file
......@@ -41,6 +41,8 @@ class TableDefs @Inject()(override protected val dbConfigProvider: DatabaseConfi
db.run(query)
}
// Queries - UserLearnsLanguage
def futureUserLearnsLanguage(user: User, language: Language): Future[Boolean] = db.run(userLearnsLanguageTQ.filter {
ull => ull.username === user.username && ull.langId === language.id
}.result.headOption.map(_.isDefined))
......@@ -63,6 +65,8 @@ class TableDefs @Inject()(override protected val dbConfigProvider: DatabaseConfi
ull => ull.username === user.username && ull.langId === language.id
}.delete).transform(_ == 1, identity)
// Queries - FlashcardToLearn View
def futureFlashcardsToLearnCount(user: User, collection: Collection): Future[Int] = db.run(flashcardsToLearnTQ.filter {
fctl => fctl.collId === collection.id && fctl.langId === collection.langId && fctl.username === user.username
}.size.result)
......@@ -104,6 +108,13 @@ ON DUPLICATE KEY UPDATE date_answered = NOW(), correct = $correct,
db.run(query).transform(_ == 1, identity)
}
// Queries - UserAnsweredFlashcard
def futureUserAnswerForFlashcard(user: User, flashcard: Flashcard): Future[Option[UserAnsweredFlashcard]] =
db.run(usersAnsweredFlashcardsTQ.filter {
uaf => uaf.username === user.username && uaf.cardId === flashcard.id && uaf.collId === flashcard.collId && uaf.langId === flashcard.langId
}.result.headOption)
// Column types
private implicit val myDateColumnType: BaseColumnType[LocalDate] =
......
@import model.{CardType, CompleteFlashcard, User}
@import model.{CardType, CompleteFlashcard, User, UserAnsweredFlashcard}
@(user: User, completeFlashcard: CompleteFlashcard, isRepeating: Boolean)(implicit requestHeader: RequestHeader, messagesProvider: MessagesProvider)
@(user: User, completeFlashcard: CompleteFlashcard, oldAnswer: Option[UserAnsweredFlashcard], isRepeating: Boolean)(
implicit requestHeader: RequestHeader, messagesProvider: MessagesProvider)
@title = @{
"Lernen"
......@@ -22,47 +23,52 @@
@helper.CSRF.formField
<section id="flashcardDiv" data-cardtype="@completeFlashcard.flashcard.cardType">
<div class="card-panel">
<h4 class="center-align">@completeFlashcard.flashcard.question</h4>
</div>
<div class="row">
<div class="col s12 l8 offset-l2" id="flashcardDiv" data-cardtype="@completeFlashcard.flashcard.cardType">
<div class="card-panel">
<h4 class="center-align">@completeFlashcard.flashcard.question</h4>
</div>
<br>
<p class="center-align">
<code>
Stapel: @oldAnswer.map(_.bucketId).getOrElse("-"), Versuch: <span id="triesSpan">@oldAnswer.map(_.tries).getOrElse(0)</span></code>
</p>
@completeFlashcard.flashcard.cardType match {
case CardType.Vocable | CardType.Text => {
<div class="row">
<div class="input-field col s12">
<input type="text" id="translation_input">
<label for="translation_input">Übersetzung</label>
@completeFlashcard.flashcard.cardType match {
case CardType.Vocable | CardType.Text => {
<div class="row">
<div class="input-field col s12">
<input type="text" id="translation_input">
<label for="translation_input">Übersetzung</label>
</div>
</div>
</div>
}
case CardType.SingleChoice | CardType.MultipleChoice => {
@for(answer <- scala.util.Random.shuffle(completeFlashcard.choiceAnswers)) {
<p>
<label for="choice_@answer.id">
<input id="choice_@answer.id" name="choice_answers" type="@choiceInputType" data-choiceid="@answer.id">
<span>@answer.answer</span>
</label>
</p>
}
case CardType.SingleChoice | CardType.MultipleChoice => {
@for(answer <- scala.util.Random.shuffle(completeFlashcard.choiceAnswers)) {
<p>
<label for="choice_@answer.id">
<input id="choice_@answer.id" name="choice_answers" type="@choiceInputType" data-choiceid="@answer.id">
<span>@answer.answer</span>
</label>
</p>
}
}
}
}
<p id="correctionTextPar">&nbsp;</p>
<p id="correctionTextPar">&nbsp;</p>
</section>
<button class="btn btn-large waves-effect green" onclick="checkSolution();" id="checkSolutionBtn"
data-href="@routes.HomeController.checkSolution(completeFlashcard.flashcard.langId, completeFlashcard.flashcard.collId, completeFlashcard.flashcard.id)">
Lösung testen
</button>
<br>
<a href="@routes.HomeController.startLearning(completeFlashcard.flashcard.langId, completeFlashcard.flashcard.collId, isRepeating)"
id="nextFlashcardBtn" class="btn btn-large waves-effect blue disabled">Weiter</a>
<div class="row" id="btnsRow">
<button class="btn btn-large waves-effect green" onclick="checkSolution();" id="checkSolutionBtn"
data-href="@routes.HomeController.checkSolution(completeFlashcard.flashcard.langId, completeFlashcard.flashcard.collId, completeFlashcard.flashcard.id)">
Lösung testen
</button>
<a href="@routes.HomeController.startLearning(completeFlashcard.flashcard.langId, completeFlashcard.flashcard.collId, isRepeating)"
id="nextFlashcardBtn" class="btn btn-large waves-effect blue disabled">Weiter</a>
</div>
</div>
<br>
}
......@@ -90,12 +90,13 @@ create table if not exists buckets
distance_days int not null
);
-- distance_days == 3 ^ (id - 1)
insert into buckets (id, distance_days)
values (1, 1),
(2, 3),
(3, 10),
(4, 30),
(5, 90);
(3, 9),
(4, 27),
(5, 81);
-- Flashcard collections
......
# Routes
GET / controllers.HomeController.index
GET / controllers.HomeController.index
# User management
GET /registerForm controllers.LoginController.registerForm
POST /register controllers.LoginController.register
GET /loginForm controllers.LoginController.loginForm
POST /login controllers.LoginController.login
GET /logout controllers.LoginController.logout
GET /registerForm controllers.LoginController.registerForm
POST /register controllers.LoginController.register
GET /loginForm controllers.LoginController.loginForm
POST /login controllers.LoginController.login
GET /logout controllers.LoginController.logout
+ nocsrf
POST /lti controllers.LoginController.lti
POST /lti controllers.LoginController.lti
# Languages
GET /languages controllers.HomeController.allLanguages
GET /languages/:langId controllers.HomeController.language(langId: Int)
GET /languages controllers.HomeController.allLanguages
GET /languages/:langId controllers.HomeController.language(langId: Int)
GET /selectLanguage/:langId controllers.HomeController.selectLanguage(langId: Int)
GET /deselectLanguage/:langId controllers.HomeController.deselectLanguage(langId: Int)
GET /selectLanguage/:langId controllers.HomeController.selectLanguage(langId: Int)
GET /deselectLanguage/:langId controllers.HomeController.deselectLanguage(langId: Int)
GET /languages/:langId/collections/:collId controllers.HomeController.collection(langId: Int, collId: Int)
GET /languages/:langId/collections/:collId controllers.HomeController.collection(langId: Int, collId: Int)
GET /languages/:langId/collections/:collId/startLearning/:isRepeating controllers.HomeController.startLearning(langId: Int, collId: Int, isRepeating: Boolean)
GET /languages/:langId/collections/:collId/cards/:cardId/learn/:isRepeating controllers.HomeController.learn(langId: Int, collId: Int, cardId:Int, isRepeating: Boolean)
GET /languages/:langId/collections/:collId/startLearning controllers.HomeController.startLearning(langId: Int, collId: Int, isRepeating: Boolean = false)
GET /languages/:langId/collections/:collId/startRepeating controllers.HomeController.startLearning(langId: Int, collId: Int, isRepeating: Boolean = true)
POST /languages/:langId/collections/:collId/cards/:cardId/checkSolution controllers.HomeController.checkSolution(langId:Int, collId: Int, cardId: Int)
GET /languages/:langId/collections/:collId/cards/:cardId/learn controllers.HomeController.learn(langId: Int, collId: Int, cardId:Int, isRepeating: Boolean = false)
GET /languages/:langId/collections/:collId/cards/:cardId/repeat controllers.HomeController.learn(langId: Int, collId: Int, cardId:Int, isRepeating: Boolean = true)
POST /languages/:langId/collections/:collId/cards/:cardId/checkSolution controllers.HomeController.checkSolution(langId:Int, collId: Int, cardId: Int)
# Admin routes
GET /admin controllers.AdminController.index
GET /admin controllers.AdminController.index
POST /newLanguage controllers.AdminController.newLanguage
POST /newLanguage controllers.AdminController.newLanguage
GET /languages/:langId/admin controllers.AdminController.languageAdmin(langId: Int)
GET /languages/:langId/admin controllers.AdminController.languageAdmin(langId: Int)
POST /languages/:langId/newCollection controllers.AdminController.newCollection(langId: Int)
POST /languages/:langId/newCollection controllers.AdminController.newCollection(langId: Int)
GET /languages/:langId/collections/:collId/admin controllers.AdminController.collectionAdmin(langId: Int, collId: Int)
GET /languages/:langId/collections/:collId/admin controllers.AdminController.collectionAdmin(langId: Int, collId: Int)
POST /languages/:langId/collection/:collId/uploadCardsFile controllers.AdminController.uploadCardsFile(langId: Int, collId: Int)
POST /languages/:langId/collection/:collId/uploadCardsFile controllers.AdminController.uploadCardsFile(langId: Int, collId: Int)
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment