Commit 8367fb0a authored by Björn Eyselein's avatar Björn Eyselein
Browse files

Update...

parent d1c30fd8
......@@ -17,7 +17,7 @@ interface AnswerSelectionResult {
interface CorrectionResult {
correct: boolean,
cardType: 'Vocable' | 'Text' | 'SingleChoice' | 'MultipleChoice'
cardType: 'Vocable' | 'Text' | 'Blank' | 'SingleChoice' | 'MultipleChoice'
learnerSolution: Solution,
operations: EditOperation[],
answerSelection: AnswerSelectionResult
......@@ -28,11 +28,13 @@ interface CorrectionResult {
let correctionTextPar: JQuery<HTMLParagraphElement>;
let checkSolutionBtn: JQuery<HTMLButtonElement>;
let checkSolutionUrl: string;
let canSolve: boolean = true;
function readSolution(cardType: string): Solution | null {
switch (cardType) {
case 'Vocable':
case 'Text':
case 'Blank':
const learnerSolution: string = $('#translation_input').val() as string;
if (learnerSolution.length === 0) {
......@@ -71,6 +73,8 @@ function onCorrectionSuccess(result: CorrectionResult): void {
correctionText += ` Die korrekte Lösung lautet '<code>${result.maybeSampleSol}</code>'.`;
}
canSolve = !result.correct && result.newTriesCount <= 2;
correctionTextPar.html(correctionText).removeClass(result.correct ? 'red-text' : 'green-text').addClass(result.correct ? 'green-text' : 'red-text');
checkSolutionBtn.prop('disabled', result.correct || (result.newTriesCount >= 2));
......@@ -85,6 +89,7 @@ function onCorrectionSuccess(result: CorrectionResult): void {
switch (result.cardType) {
case 'Vocable':
case 'Text':
case 'Blank':
$('#translation_input').removeClass(result.correct ? 'invalid' : 'valid').addClass(result.correct ? 'valid' : 'invalid');
break;
case 'SingleChoice':
......@@ -131,4 +136,14 @@ $(() => {
correctionTextPar = $('#correctionTextPar');
checkSolutionBtn = $('#checkSolutionBtn');
checkSolutionUrl = checkSolutionBtn.data('href');
$(window).bind('keypress', (event) => {
if (event.keyCode === 13) {
if (canSolve) {
checkSolution();
} else {
document.getElementById('nextFlashcardBtn').click();
}
}
});
});
\ No newline at end of file
......@@ -2,6 +2,7 @@ package controllers
import javax.inject.{Inject, Singleton}
import model._
import model.persistence.TableDefs
import play.api.data.Form
import play.api.libs.Files.TemporaryFile
import play.api.mvc._
......@@ -81,6 +82,8 @@ class AdminController @Inject()(cc: ControllerComponents, protected val tableDef
case Some(filePart: MultipartFormData.FilePart[TemporaryFile]) =>
val (failureStrings, importedFlashcards) = Importer.importFlashcards(langId, collId, filePart.ref.path)
failureStrings.foreach(println)
val futureImportedFlashcardsSaved = Future.sequence(importedFlashcards.map(
tableDefs.futureInsertCompleteFlashcard
))
......
package controllers
import model._
import model.persistence.TableDefs
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
......
......@@ -5,6 +5,7 @@ import java.time.temporal.ChronoUnit
import javax.inject.{Inject, Singleton}
import model._
import model.persistence.TableDefs
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
......
......@@ -5,6 +5,7 @@ import javax.inject._
import model.RegisterFormValues
import model.Consts._
import model._
import model.persistence.TableDefs
import play.api.data.Form
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import play.api.mvc._
......
......@@ -2,7 +2,7 @@ package controllers
import model.User
import model.Consts.idName
import model.TableDefs
import model.persistence.TableDefs
import play.api.mvc._
import scala.concurrent.{ExecutionContext, Future}
......
......@@ -35,7 +35,7 @@ object Corrector {
def correct(completeFlashcard: CompleteFlashcard, solution: Solution, previousTriesCount: Int): CorrectionResult = {
val (correct, editOps, ansSelection) = completeFlashcard.cardType match {
case CardType.Vocable | CardType.Text =>
case CardType.Vocable | CardType.Text | CardType.Blank =>
val editOperations = correctTextualFlashcard(completeFlashcard, solution, previousTriesCount)
......
......@@ -2,7 +2,7 @@ package model
import better.files._
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy
import org.apache.poi.ss.usermodel.{CellType, Row}
import org.apache.poi.ss.usermodel.{CellType, Row => ExcelRow}
import org.apache.poi.xssf.usermodel.XSSFWorkbook
object Importer {
......@@ -14,17 +14,18 @@ object Importer {
private val meaningCellIndex : Int = 2
private def cardTypeFromString(cardTypeStr: String): Either[String, CardType] = cardTypeStr match {
case "Wort" => Right(CardType.Vocable)
case "Text" => Right(CardType.Text)
case "SC" => Right(CardType.SingleChoice)
case "MC" => Right(CardType.MultipleChoice)
case other => Left(other)
case "Wort" => Right(CardType.Vocable)
case "Text" => Right(CardType.Text)
case "SC" => Right(CardType.SingleChoice)
case "MC" => Right(CardType.MultipleChoice)
case "Lücke" => Right(CardType.Blank)
case other => Left(s"Der Kartentype $other kann nicht verstanden werden!")
}
private def partitionEitherSeq[T, U](a: Seq[Either[T, U]]): (Seq[T], Seq[U]) =
a.foldLeft[(Seq[T], Seq[U])]((Seq[T](), Seq[U]())) { (b, a) =>
a match {
case Left(t) => (b._1 :+ t, b._2);
case Left(t) => (b._1 :+ t, b._2)
case Right(u) => (b._1, b._2 :+ u)
}
}
......@@ -36,7 +37,7 @@ object Importer {
val firstRowWithoutHeaderInex = sheet.getFirstRowNum + 1
// Ignore header row
// Ignore header ExcelRow
val readFlashcards = (firstRowWithoutHeaderInex to sheet.getLastRowNum) flatMap { rowIndex =>
Option(sheet.getRow(rowIndex)) match {
case None => None
......@@ -49,14 +50,14 @@ object Importer {
partitionEitherSeq(readFlashcards)
}
private def readChoiceRow(row: Row, langId: Int, collId: Int, cardType: CardType, question: String): Either[String, CompleteFlashcard] = {
private def readChoiceRow(row: ExcelRow, langId: Int, collId: Int, cardType: CardType, question: String): Either[String, CompleteFlashcard] = {
val cardId = row.getRowNum
val lastCellNum = row.getLastCellNum
val maxCellIndex = if (lastCellNum % 2 == 1) lastCellNum + 1 else lastCellNum
val (failures, answers): (Seq[String], Seq[ChoiceAnswer]) = partitionEitherSeq((meaningCellIndex to maxCellIndex by 2).map { cellIndex =>
val (_, answers): (Seq[String], Seq[ChoiceAnswer]) = partitionEitherSeq((meaningCellIndex to maxCellIndex by 2).map { cellIndex =>
readStringCell(row, cellIndex) map { answer =>
val id = (cellIndex - meaningCellIndex) / 2
......@@ -68,7 +69,6 @@ object Importer {
}
})
failures.foreach(println)
Right(CompleteFlashcard(
Flashcard(cardId, collId, langId, cardType, question, meaning = None),
......@@ -77,15 +77,16 @@ object Importer {
}
private def readTextRow(row: Row, langId: Int, collId: Int, cardType: CardType, question: String): Either[String, CompleteFlashcard] =
private def readTextRow(row: ExcelRow, langId: Int, collId: Int, cardType: CardType, question: String): Either[String, CompleteFlashcard] =
readStringCell(row, meaningCellIndex) map { meaning =>
CompleteFlashcard(
Flashcard(row.getRowNum, collId, langId, cardType, question, Some(meaning)),
Flashcard(0 /*row.getRowNum*/ , collId, langId, cardType, question, Some(meaning)),
choiceAnswers = Seq[ChoiceAnswer]()
)
}
private def readRow(row: Row, langId: Int, collId: Int): Either[String, CompleteFlashcard] =
private def readRow(row: ExcelRow, langId: Int, collId: Int): Either[String, CompleteFlashcard] =
readStringCell(row, cardTypeCellIndex) flatMap { cardTypeString: String =>
cardTypeFromString(cardTypeString) flatMap { cardType: CardType =>
......@@ -93,8 +94,8 @@ object Importer {
readStringCell(row, questionCellIndex) flatMap { question: String =>
cardType match {
case CardType.Vocable | CardType.Text => readTextRow(row, langId, collId, cardType, question)
case CardType.SingleChoice | CardType.MultipleChoice => readChoiceRow(row, langId, collId, cardType, question)
case CardType.Vocable | CardType.Text | CardType.Blank => readTextRow(row, langId, collId, cardType, question)
case CardType.SingleChoice | CardType.MultipleChoice => readChoiceRow(row, langId, collId, cardType, question)
}
}
......@@ -103,7 +104,7 @@ object Importer {
}
private def readStringCell(row: Row, index: Int): Either[String, String] = {
private def readStringCell(row: ExcelRow, index: Int): Either[String, String] = {
val cell = row.getCell(index, MissingCellPolicy.CREATE_NULL_AS_BLANK)
cell.getCellType match {
......
......@@ -37,6 +37,8 @@ case object CardType extends PlayEnum[CardType] {
case object Text extends CardType
case object Blank extends CardType
case object SingleChoice extends CardType
case object MultipleChoice extends CardType
......
package model
package model.persistence
import java.sql.{Date => SqlDate}
import java.time.LocalDate
import javax.inject.Inject
import model.Consts._
import model.persistence.{LanguageTableDefs, UserTableDefs}
import model._
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import slick.jdbc.JdbcProfile
import slick.lifted.{ForeignKeyQuery, PrimaryKey, ProvenShape}
......
......@@ -42,7 +42,7 @@
<div class="file-field input-field">
<div class="btn">
<span>File</span>
<input type="file" name="@Consts.excelFileName">
<input type="file" name="@Consts.excelFileName" required>
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text">
......
......@@ -2,58 +2,70 @@
@(user: User, language: Language, collection: Collection, cards: Seq[CompleteFlashcard], readErrors: Seq[String])
@title = @{
"Vorschau importierte Karteikarten"
}
@title = @{
"Vorschau importierte Karteikarten"
}
@main(title, Some(user)) {
@if(cards.nonEmpty) {
@cardsPerRow = @{
3
}
<div class="row">
<div class="col s12">
<a href="@routes.AdminController.collectionAdmin(language.id, collection.id)" class="btn btn-large waves-effect waves-block green">
Zurück zur Sammlung
</a>
</div>
@main(title, Some(user)) {
@if(cards.nonEmpty) {
<div class="row">
<div class="col s12">
<a href="@routes.AdminController.collectionAdmin(language.id, collection.id)" class="btn btn-large waves-effect waves-block green">
Zurück zur Sammlung
</a>
</div>
</div>
@for(completeCardGroup <- cards.grouped(2)) {
<div class="row">
@for(completeCard <- completeCardGroup) {
<div class="col m6">
<div class="card-panel">
<p>
@{
s"${completeCard.langId}.${completeCard.collId}.${completeCard.id}"
}: @completeCard.question
</p>
@completeCard.cardType match {
case CardType.Vocable | CardType.Text => {
<p>Lösung: @completeCard.meaning.getOrElse("")</p>
}
case CardType.SingleChoice | CardType.MultipleChoice => {
<hr>
@for(answer <- completeCard.choiceAnswers) {
<p><b>@if(answer.correctness != model.Correctness.Wrong) {
+
} else {
-
}</b>
@for(completeCardGroup <- cards.grouped(cardsPerRow)) {
<div class="row">
@for(completeCard <- completeCardGroup) {
<div class="col m@{
12 / cardsPerRow
} s12">
<div class="card-panel">
<p>
@{
s"${completeCard.langId}.${completeCard.collId}.${completeCard.id}"
}: @completeCard.question
</p>
@completeCard.cardType match {
case CardType.Vocable | CardType.Text | CardType.Blank => {
<p>Lösung: @completeCard.meaning.getOrElse("")</p>
}
case CardType.SingleChoice | CardType.MultipleChoice => {
<hr>
@for(answerGroup <- completeCard.choiceAnswers.grouped(2)) {
<div class="row">
@for(answer <- answerGroup) {
<div class="col m6 s12">
<b>@if(answer.correctness != model.Correctness.Wrong) {
+
} else {
-
}</b>
@answer.answer
</p>
</div>
}
</div>
}
}
}
</div>
</div>
}
</div>
}
} else {
<p class="red-text">Konnte keine Karteikarten importieren!</p>
</div>
}
}
\ No newline at end of file
} else {
<p class="red-text">Konnte keine Karteikarten importieren!</p>
}
}
\ No newline at end of file
......@@ -20,7 +20,7 @@
}
@main(title, Some(user), scripts) {
@*<form onsubmit="checkSolution(); return false;">*@
@helper.CSRF.formField
<div class="row">
......@@ -35,10 +35,10 @@
</p>
@completeFlashcard.cardType match {
case CardType.Vocable | CardType.Text => {
case CardType.Vocable | CardType.Text | CardType.Blank => {
<div class="row">
<div class="input-field col s12">
<input type="text" id="translation_input">
<input type="text" id="translation_input" autofocus autocomplete="off">
<label for="translation_input">Übersetzung</label>
</div>
</div>
......@@ -67,8 +67,7 @@
</div>
</div>
@*</form>*@
<br>
}
......@@ -118,13 +118,12 @@ values (1, 1, 'Beispielsammlung');
create table if not exists flashcards
(
id int not null auto_increment,
coll_id int not null,
lang_id int not null,
flash_card_type enum ('Vocable', 'Text', 'SingleChoice', 'MultipleChoice') not null default 'Vocable',
question text not null,
meaning text,
id int not null auto_increment,
coll_id int not null,
lang_id int not null,
flash_card_type enum ('Vocable', 'Text', 'Blank', 'SingleChoice', 'MultipleChoice') not null default 'Vocable',
question text not null,
meaning text, -- can be null!
primary key (id, coll_id, lang_id),
foreign key (coll_id, lang_id) references collections (id, lang_id)
......
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