Commit 4a477667 authored by Björn Eyselein's avatar Björn Eyselein
Browse files

Start großes Update: Sprachen gegen Kurse tauschen

parent 660be1cb
......@@ -15,30 +15,69 @@ class AdminController @Inject()(cc: ControllerComponents, protected val tableDef
def index: EssentialAction = futureWithUser(adminRightsRequired = true) { admin =>
implicit request =>
tableDefs.futureAllLanguages map {
allLanguages => Ok(views.html.admin.adminIndex(admin, allLanguages, FormMappings.newLanguageValuesForm))
tableDefs.futureAllCourses map {
allCourses => Ok(views.html.admin.adminIndex(admin, allCourses /*, FormMappings.newLanguageValuesForm*/))
}
}
def languageAdmin(langId: Int): EssentialAction = futureWithUserAndLanguage(adminRightsRequired = true, langId) { (admin, language) =>
def courseAdmin(courseId: String): EssentialAction = futureWithUserAndCourse(adminRightsRequired = true, courseId) { (admin, course) =>
implicit request =>
tableDefs.futureCollectionsForCourse(course) map {
collections => Ok(views.html.admin.courseAdmin(admin, course, collections))
}
}
def allocateCollectionsToCourseForm(courseId: String): EssentialAction = futureWithUserAndCourse(adminRightsRequired = true, courseId) { (admin, course) =>
implicit request =>
tableDefs.futureCollectionsAndCourseImportState(course) map {
collectionsAndCourseImportState => Ok(views.html.admin.allocateCollectionsToCourseForm(admin, course, collectionsAndCourseImportState))
}
}
def allocateCollectionToCourse(courseId: String, collId: Int): EssentialAction = futureWithUserAndCourse(adminRightsRequired = true, courseId) { (admin, course) =>
implicit request =>
tableDefs.futureCollectionsForLanguage(language) map {
collections => Ok(views.html.admin.languageAdmin(admin, language, collections, FormMappings.newCollectionValuesForm))
tableDefs.futureCollectionById(collId) flatMap {
case None => ???
case Some(collection) =>
tableDefs.allocateCollectionToCourse(course, collection) map {
case true => Redirect(routes.AdminController.allocateCollectionsToCourseForm(course.id))
case false => ???
}
}
}
def collectionAdmin(langId: Int, collId: Int): EssentialAction = futureWithUserAndCollection(adminRightsRequired = true, langId, collId) { (admin, language, collection) =>
def deallocateCollectionFromCourse(courseId: String, collId: Int) : EssentialAction = futureWithUserAndCourse(adminRightsRequired = true, courseId) {(admin, course) =>
implicit request =>
tableDefs.futureCollectionById(collId) flatMap {
case None => ???
case Some(collection) =>
tableDefs.deallocateCollectionFromCourse(course, collection) map {
case true => Redirect(routes.AdminController.allocateCollectionsToCourseForm(courseId))
case false => ???
}
}
}
def languageAdmin(langId: Int): EssentialAction = futureWithUserAndLanguage(adminRightsRequired = true, langId) { (admin, language) =>
implicit request =>
???
// tableDefs.futureCollectionsForLanguage(language) map {
// collections => Ok(views.html.admin.languageAdmin(admin, language, collections, FormMappings.newCollectionValuesForm))
// }
}
def collectionAdmin(collId: Int): EssentialAction = futureWithUserAndCollection(adminRightsRequired = true, collId) { (admin, collection) =>
implicit request =>
tableDefs.futureFlashcardsForCollection(collection) map {
flashcards => Ok(views.html.admin.collectionAdmin(admin, language, collection, flashcards))
flashcards => Ok(views.html.admin.collectionAdmin(admin, collection, flashcards))
}
}
def newLanguage: EssentialAction = futureWithUser(adminRightsRequired = true) { admin =>
implicit request =>
def onError: Form[String] => Future[Result] = { formWithErrors =>
tableDefs.futureAllLanguages map {
allLanguages => BadRequest(views.html.admin.adminIndex(admin, allLanguages, formWithErrors))
tableDefs.futureAllCourses map {
allCourses => BadRequest(views.html.admin.adminIndex(admin, allCourses /*, formWithErrors*/))
}
}
......@@ -53,34 +92,35 @@ class AdminController @Inject()(cc: ControllerComponents, protected val tableDef
FormMappings.newLanguageValuesForm.bindFromRequest.fold(onError, onRead)
}
def newCollection(langId: Int): EssentialAction = futureWithUserAndLanguage(adminRightsRequired = true, langId) { (admin, language) =>
def newCollection: EssentialAction = futureWithUser(adminRightsRequired = true) { admin =>
implicit request =>
def onError: Form[String] => Future[Result] = { formWithErrors =>
tableDefs.futureCollectionsForLanguage(language) map {
collections => BadRequest(views.html.admin.languageAdmin(admin, language, collections, formWithErrors))
}
// tableDefs.futureCollectionsForLanguage(language) map {
// collections => BadRequest(views.html.admin.languageAdmin(admin, language, collections, formWithErrors))
// }
???
}
def onRead: String => Future[Result] = { newCollectionName =>
val newCollection = Collection(-1, langId, newCollectionName)
val newCollection = Collection(-1, newCollectionName)
tableDefs.futureInsertCollection(newCollection) map {
newCollId => Redirect(routes.AdminController.collectionAdmin(langId, newCollId))
newCollId => Redirect(routes.AdminController.collectionAdmin(newCollId))
}
}
FormMappings.newCollectionValuesForm.bindFromRequest.fold(onError, onRead)
}
def uploadCardsFile(langId: Int, collId: Int): EssentialAction = futureWithUserAndCollection(adminRightsRequired = true, langId, collId) { (admin, language, collection) =>
def uploadCardsFile(collId: Int): EssentialAction = futureWithUserAndCollection(adminRightsRequired = true, collId) { (admin, collection) =>
implicit request =>
request.body.asMultipartFormData flatMap (_.file(Consts.excelFileName)) match {
case None => Future(Redirect(routes.AdminController.collectionAdmin(langId, collId)))
case None => Future(Redirect(routes.AdminController.collectionAdmin(collId)))
case Some(filePart: MultipartFormData.FilePart[TemporaryFile]) =>
val (failureStrings, importedFlashcards) = Importer.importFlashcards(langId, collId, filePart.ref.path)
val (failureStrings, importedFlashcards) = Importer.importFlashcards(collId, filePart.ref.path)
failureStrings.foreach(println)
......@@ -89,7 +129,7 @@ class AdminController @Inject()(cc: ControllerComponents, protected val tableDef
))
futureImportedFlashcardsSaved map { importedFlashcardsSaved =>
Ok(views.html.cardPreview(admin, language, collection, importedFlashcards, failureStrings))
Ok(views.html.cardPreview(admin, collection, importedFlashcards, failureStrings))
}
}
......
......@@ -9,18 +9,21 @@ import scala.concurrent.{ExecutionContext, Future}
trait ControllerHelpers extends Secured {
self: AbstractController =>
protected implicit val ec: ExecutionContext
protected val tableDefs: TableDefs
private def onNoSuchLanguage(langId: Int): Result = NotFound(s"Es gibt keine Sprache mit der ID $langId")
private def onNoSuchCourse(courseId: String): Result = NotFound(s"Es gibt keinen Kurs mit der ID '$courseId'")
private def onNoSuchLanguage(langId: Int): Result = NotFound(s"Es gibt keine Sprache mit der ID '$langId'")
private def onNoSuchCollection(language: Language, collId: Int): Result =
NotFound(s"Es gibt keine Sammlung mit der ID $collId für die Sprache ${language.name}")
private def onNoSuchCollection(collId: Int): Result = NotFound(s"Es gibt keine Sammlung mit der ID '$collId'")
private def onNuSuchFlashcard(language: Language, collection: Collection, cardId: Int): Result =
NotFound(s"Es gibt keine Karteikarte mit der ID $cardId für die Sammlung ${collection.name} für die Sprache ${language.name}")
private def onNuSuchFlashcard(collection: Collection, cardId: Int): Result =
NotFound(s"Es gibt keine Karteikarte mit der ID '$cardId' für die Sammlung '${collection.name}'!")
protected def withUserAndLanguage(adminRightsRequired: Boolean, langId: Int)(f: (User, Language) => Request[AnyContent] => Result)
(implicit ec: ExecutionContext): EssentialAction =
protected def withUserAndLanguage(adminRightsRequired: Boolean, langId: Int)
(f: (User, Language) => Request[AnyContent] => Result): EssentialAction =
futureWithUser(adminRightsRequired) { user =>
implicit request =>
tableDefs.futureLanguageById(langId) map {
......@@ -29,56 +32,75 @@ trait ControllerHelpers extends Secured {
}
}
protected def futureWithUserAndLanguage(adminRightsRequired: Boolean, langId: Int)(f: (User, Language) => Request[AnyContent] => Future[Result])
(implicit ec: ExecutionContext): EssentialAction =
protected def futureWithUserAndLanguage(adminRightsRequired: Boolean, langId: Int)
(f: (User, Language) => Request[AnyContent] => Future[Result]): EssentialAction =
futureWithUser(adminRightsRequired) { user =>
implicit request =>
tableDefs.futureLanguageById(langId) flatMap {
case None => Future(onNoSuchLanguage(langId))
case None => Future.successful(onNoSuchLanguage(langId))
case Some(language) => f(user, language)(request)
}
}
protected def withUserAndCollection(adminRightsRequired: Boolean, langId: Int, collId: Int)(f: (User, Language, Collection) => Request[AnyContent] => Result)
(implicit ec: ExecutionContext): EssentialAction =
futureWithUserAndLanguage(adminRightsRequired, langId) { (user, language) =>
protected def withUserAndCourse(adminRightsRequired: Boolean, courseId: String)
(f: (User, Course) => Request[AnyContent] => Result): EssentialAction =
futureWithUser(adminRightsRequired) { user =>
implicit request =>
tableDefs.futureCourseById(courseId) map {
case None => onNoSuchCourse(courseId)
case Some(course) => f(user, course)(request)
}
}
protected def futureWithUserAndCourse(adminRightsRequired: Boolean, courseId: String)
(f: (User, Course) => Request[AnyContent] => Future[Result]): EssentialAction =
futureWithUser(adminRightsRequired) { user =>
implicit request =>
tableDefs.futureCollectionById(language, collId) map {
case None => onNoSuchCollection(language, collId)
case Some(collection) => f(user, language, collection)(request)
tableDefs.futureCourseById(courseId) flatMap {
case None => Future.successful(onNoSuchCourse(courseId))
case Some(course) => f(user, course)(request)
}
}
protected def futureWithUserAndCollection(adminRightsRequired: Boolean, langId: Int, collId: Int)(f: (User, Language, Collection) => Request[AnyContent] => Future[Result])
(implicit ec: ExecutionContext): EssentialAction =
futureWithUserAndLanguage(adminRightsRequired, langId) { (user, language) =>
protected def withUserAndCollection(adminRightsRequired: Boolean, collId: Int)
(f: (User, Collection) => Request[AnyContent] => Result): EssentialAction =
futureWithUser(adminRightsRequired) { user =>
implicit request =>
tableDefs.futureCollectionById(collId) map {
case None => onNoSuchCollection(collId)
case Some(collection) => f(user, collection)(request)
}
}
protected def futureWithUserAndCollection(adminRightsRequired: Boolean, collId: Int)
(f: (User, Collection) => Request[AnyContent] => Future[Result]): EssentialAction =
futureWithUser(adminRightsRequired) { user =>
implicit request =>
tableDefs.futureCollectionById(language, collId) flatMap {
case None => Future(onNoSuchCollection(language, collId))
case Some(collection) => f(user, language, collection)(request)
tableDefs.futureCollectionById(collId) flatMap {
case None => Future.successful(onNoSuchCollection(collId))
case Some(collection) => f(user, collection)(request)
}
}
protected def withUserAndCompleteFlashcard(adminRightsRequired: Boolean, langId: Int, collId: Int, cardId: Int)
(f: (User, Language, Collection, Flashcard) => Request[AnyContent] => Result)
(implicit ec: ExecutionContext): EssentialAction =
futureWithUserAndCollection(adminRightsRequired, langId, collId) { (user, language, collection) =>
(f: (User, Collection, Flashcard) => Request[AnyContent] => Result): EssentialAction =
futureWithUserAndCollection(adminRightsRequired, collId) { (user, collection) =>
implicit request =>
tableDefs.futureFlashcardById(collection, cardId) map {
case None => onNuSuchFlashcard(language, collection, cardId)
case Some(flashcard) => f(user, language, collection, flashcard)(request)
case None => onNuSuchFlashcard(collection, cardId)
case Some(flashcard) => f(user, collection, flashcard)(request)
}
}
protected def futureWithUserAndCompleteFlashcard(adminRightsRequired: Boolean, langId: Int, collId: Int, cardId: Int)
(f: (User, Language, Collection, Flashcard) => Request[AnyContent] => Future[Result])
(implicit ec: ExecutionContext): EssentialAction =
futureWithUserAndCollection(adminRightsRequired, langId, collId) { (user, language, collection) =>
protected def futureWithUserAndCompleteFlashcard(adminRightsRequired: Boolean, collId: Int, cardId: Int)
(f: (User, Collection, Flashcard) => Request[AnyContent] => Future[Result]): EssentialAction =
futureWithUserAndCollection(adminRightsRequired, collId) { (user, collection) =>
implicit request =>
tableDefs.futureFlashcardById(collection, cardId) flatMap {
case None => Future(onNuSuchFlashcard(language, collection, cardId))
case Some(flashcard) => f(user, language, collection, flashcard)(request)
case None => Future.successful(onNuSuchFlashcard(collection, cardId))
case Some(flashcard) => f(user, collection, flashcard)(request)
}
}
......
......@@ -15,8 +15,15 @@ class HomeController @Inject()(cc: ControllerComponents, protected val tableDefs
def index: EssentialAction = futureWithUser(adminRightsRequired = false) { user =>
implicit request =>
tableDefs.futureLanguagesForUser(user) map { languages =>
Ok(views.html.myLanguages(user, languages))
tableDefs.futureCoursesForUser(user) map { courses =>
Ok(views.html.index(user, courses))
}
}
def userPage: EssentialAction = futureWithUser(adminRightsRequired = false) { user =>
implicit request =>
tableDefs.futureCoursesForUser(user) map {
courses => Ok(views.html.user(user, courses))
}
}
......@@ -27,12 +34,20 @@ class HomeController @Inject()(cc: ControllerComponents, protected val tableDefs
}
}
def course(courseId: String): EssentialAction = futureWithUserAndCourse(adminRightsRequired = false, courseId) { (user, course) =>
implicit request =>
tableDefs.futureCollectionsForCourse(course) map {
collectionsForCourse => Ok(views.html.course(user, course,collectionsForCourse))
}
}
def language(langId: Int): EssentialAction =
futureWithUserAndLanguage(adminRightsRequired = false, langId) { (user, language) =>
implicit request =>
tableDefs.futureCollectionsForLanguage(language) map {
collections => Ok(views.html.language(user, language, collections))
}
// tableDefs.futureCollectionsForLanguage(language) map {
// collections => Ok(views.html.language(user, language, collections))
// }
???
}
def selectLanguage(langId: Int): EssentialAction =
......@@ -53,19 +68,19 @@ class HomeController @Inject()(cc: ControllerComponents, protected val tableDefs
}
}
def collection(langId: Int, collId: Int): EssentialAction =
futureWithUserAndCollection(adminRightsRequired = false, langId, collId) { (user, language, collection) =>
def collection(collId: Int): EssentialAction =
futureWithUserAndCollection(adminRightsRequired = false, collId) { (user, collection) =>
implicit request =>
for {
flashcardCount <- tableDefs.futureFlashcardCountForCollection(collection)
toLearnCount <- tableDefs.futureFlashcardsToLearnCount(user, collection)
toRepeatCount <- tableDefs.futureFlashcardsToRepeatCount(user, collection)
} yield Ok(views.html.collection(user, language, collection, flashcardCount, toLearnCount, toRepeatCount))
} yield Ok(views.html.collection(user, collection, flashcardCount, toLearnCount, toRepeatCount))
}
def startLearning(langId: Int, collId: Int, isRepeating: Boolean): EssentialAction =
futureWithUserAndCollection(adminRightsRequired = false, langId, collId) { (user, _, collection) =>
def startLearning(collId: Int, isRepeating: Boolean): EssentialAction =
futureWithUserAndCollection(adminRightsRequired = false, collId) { (user, collection) =>
implicit request =>
val futureFlashcard = if (isRepeating)
tableDefs.futureMaybeIdentifierNextFlashcardToRepeat(user, collection)
......@@ -73,13 +88,13 @@ class HomeController @Inject()(cc: ControllerComponents, protected val tableDefs
tableDefs.futureMaybeIdentifierNextFlashcardToLearn(user, collection)
futureFlashcard map {
case None => Redirect(routes.HomeController.collection(langId, collId))
case Some(identifier) => Redirect(routes.HomeController.learn(identifier.langId, identifier.collId, identifier.cardId, isRepeating))
case None => Redirect(routes.HomeController.collection(collId))
case Some(identifier) => Redirect(routes.HomeController.learn(identifier.collId, identifier.cardId, isRepeating))
}
}
def learn(langId: Int, collId: Int, cardId: Int, isRepeating: Boolean): EssentialAction =
futureWithUserAndCompleteFlashcard(adminRightsRequired = false, langId, collId, cardId) { (user, _, _, flashcard) =>
def learn(collId: Int, cardId: Int, isRepeating: Boolean): EssentialAction =
futureWithUserAndCompleteFlashcard(adminRightsRequired = false, collId, cardId) { (user, _, flashcard) =>
implicit request =>
val futureMaybeOldAnswer: Future[Option[UserAnsweredFlashcard]] =
tableDefs.futureUserAnswerForFlashcard(user, flashcard)
......@@ -88,15 +103,15 @@ class HomeController @Inject()(cc: ControllerComponents, protected val tableDefs
futureMaybeOldAnswer map { maybeOldAnswer =>
if (!isRepeating && maybeOldAnswer.isDefined) {
// TODO: Something went wrong, take next flashcard?
Redirect(routes.HomeController.startLearning(langId, collId, isRepeating))
Redirect(routes.HomeController.startLearning(collId, isRepeating))
} else {
Ok(views.html.learn(user, flashcard, maybeOldAnswer, isRepeating))
}
}
}
def checkSolution(langId: Int, collId: Int, cardId: Int): EssentialAction =
futureWithUserAndCompleteFlashcard(adminRightsRequired = false, langId, collId, cardId) { (user, _, _, flashcard) =>
def checkSolution(collId: Int, cardId: Int): EssentialAction =
futureWithUserAndCompleteFlashcard(adminRightsRequired = false, collId, cardId) { (user, _, flashcard) =>
implicit request =>
request.body.asJson flatMap (json => JsonFormats.solutionFormat.reads(json).asOpt) match {
case None => Future(BadRequest("Could not read solution..."))
......
......@@ -32,6 +32,7 @@ class LoginController @Inject()(cc: ControllerComponents, val dbConfigProvider:
for {
user <- selectOrInsertUser(ltiFormValues.username)
course <- selectOrInsertCourse(ltiFormValues.courseIdentifier, ltiFormValues.courseName)
maybePw <- tableDefs.futurePwHashForUser(user)
_ <- selectOrInsertUserInCourse(user, course)
} yield Redirect(routes.HomeController.index()).withSession(idName -> user.username)
......@@ -101,7 +102,7 @@ class LoginController @Inject()(cc: ControllerComponents, val dbConfigProvider:
tableDefs.futureUserByUserName(credentials.username) flatMap {
case None => Future(Redirect(controllers.routes.LoginController.registerForm()))
case Some(user) => tableDefs.futurePwHashForUser(credentials.username) map {
case Some(user) => tableDefs.futurePwHashForUser(user) map {
case None => BadRequest("Cannot change password!")
case Some(userPassword) =>
if (credentials.password isBcrypted userPassword.pwHash) {
......
......@@ -30,7 +30,7 @@ object Corrector {
private def correctChoiceFlashcard(flashcard: ChoiceFlashcard, solution: Solution, previousTriesCount: Int): AnswerSelectionResult = {
val selectedAnswerIds: Seq[Int] = solution.selectedAnswers
val correctAnswerIds: Seq[Int] = flashcard.choiceAnswers.filter(_.correctness != Correctness.Wrong).map(_.id)
val correctAnswerIds: Seq[Int] = flashcard.choiceAnswers.filter(_.correctness != Correctness.Wrong).map(_.answerId)
matchAnswerIds(selectedAnswerIds, correctAnswerIds)
}
......
......@@ -4,33 +4,39 @@ sealed trait Flashcard {
val cardId : Int
val collId : Int
val langId : Int
val cardType: CardType
val question: String
def identifier: FlashcardIdentifier = FlashcardIdentifier(cardId, collId, langId)
def identifier: FlashcardIdentifier = FlashcardIdentifier(cardId, collId)
}
// Text and Words
final case class WordFlashcard(cardId: Int, collId: Int, langId: Int, question: String, meaning: String) extends Flashcard {
final case class WordFlashcard(cardId: Int, collId: Int, question: String, meaning: String) extends Flashcard {
override val cardType: CardType = CardType.Vocable
}
final case class TextFlashcard(cardId: Int, collId: Int, langId: Int, question: String, meaning: String) extends Flashcard {
final case class TextFlashcard(cardId: Int, collId: Int, question: String, meaning: String) extends Flashcard {
override val cardType: CardType = CardType.Text
}
sealed trait CardAnswer {
val answerId: Int
val cardId : Int
val collId : Int
val answer : String
}
// Blanks
final case class BlanksAnswer(answerId: Int, cardId: Int, collId: Int, langId: Int, answer: String)
final case class BlanksAnswer(answerId: Int, cardId: Int, collId: Int, answer: String) extends CardAnswer
final case class BlanksFlashcard(cardId: Int, collId: Int, langId: Int, question: String, answers: Seq[BlanksAnswer]) extends Flashcard {
final case class BlanksFlashcard(cardId: Int, collId: Int, question: String, answers: Seq[BlanksAnswer]) extends Flashcard {
override val cardType: CardType = CardType.Blank
......@@ -38,9 +44,9 @@ final case class BlanksFlashcard(cardId: Int, collId: Int, langId: Int, question
// Single and Multiple choice
final case class ChoiceAnswer(id: Int, cardId: Int, collId: Int, langId: Int, answer: String, correctness: Correctness)
final case class ChoiceAnswer(answerId: Int, cardId: Int, collId: Int, answer: String, correctness: Correctness) extends CardAnswer
final case class ChoiceFlashcard(cardId: Int, collId: Int, langId: Int, question: String, choiceAnswers: Seq[ChoiceAnswer]) extends Flashcard {
final case class ChoiceFlashcard(cardId: Int, collId: Int, question: String, choiceAnswers: Seq[ChoiceAnswer]) extends Flashcard {
val isMultipleChoice: Boolean = choiceAnswers.count(_.correctness != Correctness.Wrong) >= 2
......
......@@ -10,7 +10,7 @@ final case class RegisterFormValues(username: String, pw: String, pwRepeat: Stri
final case class NewCollectionFormValues()
final case class LtiFormValues(username: String, courseName: String, courseIdentifier: String)
final case class LtiFormValues(username: String, courseIdentifier: String, courseName: String)
object FormMappings {
......
......@@ -30,7 +30,7 @@ object Importer {
}
}
def importFlashcards(langId: Int, collId: Int, file: File = stdFile): (Seq[String], Seq[Flashcard]) = {
def importFlashcards(collId: Int, file: File = stdFile): (Seq[String], Seq[Flashcard]) = {
val workbook = new XSSFWorkbook(file.path.toAbsolutePath.toFile)
val sheet = workbook.getSheetAt(workbook.getActiveSheetIndex)
......@@ -41,7 +41,7 @@ object Importer {
val readFlashcards = (firstRowWithoutHeaderInex to sheet.getLastRowNum) flatMap { rowIndex =>
Option(sheet.getRow(rowIndex)) match {
case None => None
case Some(row) => Some(readRow(row, langId, collId))
case Some(row) => Some(readRow(row, collId))
}
}
......@@ -50,7 +50,7 @@ object Importer {
partitionEitherSeq(readFlashcards)
}
private def readChoiceRow(row: ExcelRow, langId: Int, collId: Int, question: String): Either[String, Flashcard] = {
private def readChoiceRow(row: ExcelRow, collId: Int, question: String): Either[String, Flashcard] = {
val cardId = row.getRowNum
......@@ -65,41 +65,41 @@ object Importer {
val correctness = if (correctnessCellStringValue.nonEmpty) Correctness.Correct else Correctness.Wrong
ChoiceAnswer(id, cardId, collId, langId, answer, correctness)
ChoiceAnswer(id, cardId, collId, answer, correctness)
}
})
Right(ChoiceFlashcard(cardId, collId, langId, question, answers))
Right(ChoiceFlashcard(cardId, collId, question, answers))
}
private def readWordRow(row: ExcelRow, langId: Int, collId: Int, question: String): Either[String, Flashcard] =
private def readWordRow(row: ExcelRow, collId: Int, question: String): Either[String, Flashcard] =
readStringCell(row, meaningCellIndex) map { meaning =>
WordFlashcard(0, collId, langId, question, meaning)
WordFlashcard(0, collId, question, meaning)
}
private def readTextRow(row: ExcelRow, langId: Int, collId: Int, question: String): Either[String, Flashcard] =
private def readTextRow(row: ExcelRow, collId: Int, question: String): Either[String, Flashcard] =
readStringCell(row, meaningCellIndex) map { meaning =>
TextFlashcard(0 /*row.getRowNum*/ , collId, langId, question, meaning)
TextFlashcard(0 /*row.getRowNum*/ , collId, question, meaning)
}
private def readBlankRow(row: ExcelRow, langId: Int, collId: Int, question: String): Either[String, Flashcard] = {
private def readBlankRow(row: ExcelRow, collId: Int, question: String): Either[String, Flashcard] = {
val cardId = row.getRowNum
val (_, answers): (Seq[String], Seq[BlanksAnswer]) = partitionEitherSeq((meaningCellIndex to row.getLastCellNum).map { cellIndex =>
readStringCell(row, cellIndex) map {
answer =>
val id = cellIndex - meaningCellIndex
BlanksAnswer(id, cardId, collId, langId, answer)
BlanksAnswer(id, cardId, collId, answer)
}
})
Right(BlanksFlashcard(cardId, collId, langId, question, answers))
Right(BlanksFlashcard(cardId, collId, question, answers))
}
private def readRow(row: ExcelRow, langId: Int, collId: Int): Either[String, Flashcard] =
private def readRow(row: ExcelRow, collId: Int): Either[String, Flashcard] =
readStringCell(row, cardTypeCellIndex) flatMap { cardTypeString: String =>
cardTypeFromString(cardTypeString) flatMap { cardType: CardType =>
......@@ -107,10 +107,10 @@ object Importer {
readStringCell(row, questionCellIndex) flatMap { question: String =>
cardType match {
case CardType.Vocable => readWordRow(row, langId, collId, question)
case CardType.Text => readTextRow(row, langId, collId, question)
case CardType.Blank => readBlankRow(row, langId, collId, question)
case CardType.SingleChoice | CardType.MultipleChoice => readChoiceRow(row, langId, collId, question)
case CardType.Vocable => readWordRow(row, collId, question)
case CardType.Text => readTextRow(row, collId, question)
case CardType.Blank => readBlankRow(row, collId, question)
case CardType.SingleChoice | CardType.MultipleChoice => readChoiceRow(row, collId, question)
}
}
......
......@@ -17,12 +17,23 @@ final case class UserPassword(username: String, pwHash: String)
final case class Course(id: String, name: String)
// User <-> Course
final case class UserInCourse(username: String, courseId: String)