Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b398c36
Added new lesson and retry lesson buttons
oislen Jun 15, 2025
78de761
Added stylings for new lesson and retry lesson buttons
oislen Jun 15, 2025
202e53b
Updated controller funcitonality to index on questions using lesson q…
oislen Jun 15, 2025
ed9e3c3
Added lesson question id to question model
oislen Jun 15, 2025
89109b5
Added custom sql query to pull question using lesson id and question …
oislen Jun 15, 2025
6538edf
Added custom function to check if results exist for a given lesson id
oislen Jun 15, 2025
9740f28
Extending answer service to include existsById look up
oislen Jun 15, 2025
6d9b037
Extending answer service to include existsById and existsByLessonId l…
oislen Jun 15, 2025
ebbf4f1
Extending question service with get question by lesson id and lesson …
oislen Jun 15, 2025
adc1be3
Added lesson question to model schema
oislen Jun 15, 2025
d3d67d6
Updated flowchart to show connection between lessons and results pages
oislen Jun 15, 2025
4c0ae10
Added custom message when lesson is incomplete
oislen Jun 15, 2025
6fdd776
Applying left join and ordering by question id
oislen Jun 15, 2025
47338b9
Applying left join and ordering by lesson id
oislen Jun 15, 2025
d63bd31
Removed results generation logic from new lesson view. Updating retry…
oislen Jun 15, 2025
2888c40
Added find and exists by question id to answers repository
oislen Jun 15, 2025
2f9fdf2
Corrected doc strings
oislen Jun 15, 2025
c0d8ebc
Corrected doc strings
oislen Jun 15, 2025
ce8f283
Corrected doc strings
oislen Jun 15, 2025
c7afc10
Extendd answer service to include find by and exist by qustion id fun…
oislen Jun 15, 2025
b68297b
Adjusted table column order to show solution after answer
oislen Jun 15, 2025
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
16 changes: 14 additions & 2 deletions doc/web_app_flowchart/web_app_flowchart.drawio
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" version="27.1.5">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1426" dy="751" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<mxGraphModel dx="1042" dy="535" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
Expand Down Expand Up @@ -67,12 +67,24 @@
<mxCell id="ZCydH4o10Z1AQBYC3WxA-14" value="&lt;b&gt;Lesson Workflow&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="360" y="420" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="ZCydH4o10Z1AQBYC3WxA-15" value="" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-7" target="WIyWlLk6GJQsqaUBKTNV-12" edge="1">
<mxCell id="ZCydH4o10Z1AQBYC3WxA-15" value="" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;startArrow=classic;startFill=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-7" target="WIyWlLk6GJQsqaUBKTNV-12" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="390" y="390" as="sourcePoint" />
<mxPoint x="470" y="390" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="9GQkNc22g9_RIPlS9kfo-1" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-3" target="w7i74J8MNqT3PGJUnFBG-0">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="390" y="310" as="sourcePoint" />
<mxPoint x="440" y="260" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="9GQkNc22g9_RIPlS9kfo-2" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="w7i74J8MNqT3PGJUnFBG-0" target="ZCydH4o10Z1AQBYC3WxA-0">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="390" y="310" as="sourcePoint" />
<mxPoint x="440" y="260" as="targetPoint" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
Expand Down
Binary file modified doc/web_app_flowchart/web_app_flowchart.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
189 changes: 117 additions & 72 deletions src/main/java/com/czechtutor/controller/ApplicationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public String createLesson(@ModelAttribute LessonModel lessonModel) {
lessonService.save(lessonModel);
// redirect to view
String path = String.valueOf(lessonModel.getLessonId());
String view = "/lesson/" + path;
String view = "/newLessonQuestion/" + path;
logger.info(lessonModel.getLessonPayload());
return "redirect:" + view;
}
Expand All @@ -165,43 +165,20 @@ public String createLesson(@ModelAttribute LessonModel lessonModel) {
* @param lessonId the generated lesson id path variable
* @return redirects to the lesson template with the generated lesson id
*/
@GetMapping(value = "/lesson/{lessonId}")
public String createQuestion(@PathVariable("lessonId") Integer lessonId) {
// check NQuestions for lessonId against database
Integer nQuestions = lessonService.get(lessonId).getNQuestions();
Integer nLessonQuestions = questionService.findByLessonId(lessonId).size();
if (nLessonQuestions < nQuestions) {
logger.info("~~~~~ Creating question.");
// generate a question
LessonModel lessonModel = lessonService.get(lessonId);
QuestionModel questionModel = questionService.create(lessonModel, null);
questionService.save(questionModel);
Integer questionId = questionModel.getQuestionId();
// redirect to view
String path = String.valueOf(lessonId) + "/" + String.valueOf(questionId);
String view = "/lesson/" + path;
logger.info(view);
logger.info(questionModel.getQuestionPayload());
return "redirect:" + view;
} else {
logger.info("~~~~~ Creating result.");
// define decimal formatter
DecimalFormat decimalFormatter = new DecimalFormat("#.##");
decimalFormatter.setRoundingMode(RoundingMode.HALF_EVEN);
// generate the results of the lesson
ArrayList<AnswerModel> lessonAnswers = answerService.findByLessonId(lessonId);
Integer nCorrect = resultService.countTotalCorrect(lessonAnswers);
Float score = Float.valueOf(decimalFormatter.format(Float.valueOf(nCorrect) / Float.valueOf(nQuestions) * 100));
// create a result
ResultModel resultModel = resultService.create(lessonId, nCorrect, score);
resultService.save(resultModel);
// redirect to view
String path = String.valueOf(lessonId);
String view = "/result/" + path;
logger.info(view);
logger.info(resultModel.getResultPayload());
return "redirect:" + view;
}
@GetMapping(value = "/newLessonQuestion/{lessonId}")
public String createLessonQuestion(@PathVariable("lessonId") Integer lessonId) {
logger.info("~~~~~ Creating question.");
// generate a question
LessonModel lessonModel = lessonService.get(lessonId);
QuestionModel questionModel = questionService.create(lessonModel, null);
questionService.save(questionModel);
Integer lessonQuestionId = questionModel.getLessonQuestionId();
// redirect to view
String path = String.valueOf(lessonId) + "/" + String.valueOf(lessonQuestionId);
String view = "/lesson/" + path;
logger.info(view);
logger.info(questionModel.getQuestionPayload());
return "redirect:" + view;
}

/**
Expand All @@ -210,16 +187,17 @@ public String createQuestion(@PathVariable("lessonId") Integer lessonId) {
* id</p>
*
* @param lessonId the generated lesson id path variable
* @param questionId the generated question id path variable
* @param lessonQuestionId the generated lesson question id path variable
* @param model the Model ui object for populating the lesson template with
* Thymeleaf
* @return the lesson template for the given lesson id and question id
*/
@GetMapping(value = "/lesson/{lessonId}/{questionId}")
public String getLessonPage(@PathVariable("lessonId") Integer lessonId, @PathVariable("questionId") Integer questionId, Model model) {
@GetMapping(value = "/lesson/{lessonId}/{lessonQuestionId}")
public String getLessonPage(@PathVariable("lessonId") Integer lessonId, @PathVariable("lessonQuestionId") Integer lessonQuestionId, Model model) {
logger.info("~~~~~ Redirecting to lesson.");
QuestionModel questionModel = questionService.get(questionId);
String path = String.valueOf(lessonId) + "/" + String.valueOf(questionId);
// pull lesson model for lesson id and lesson question id
QuestionModel questionModel = questionService.getQuestionByLessonQuestionIds(lessonId, lessonQuestionId);
String path = String.valueOf(lessonId) + "/" + String.valueOf(lessonQuestionId);
model.addAttribute("questionModel", questionModel);
model.addAttribute("answerModel", new AnswerModel());
model.addAttribute("path", path);
Expand All @@ -232,25 +210,75 @@ public String getLessonPage(@PathVariable("lessonId") Integer lessonId, @PathVar
* Posts user input from the lesson template page</p>
*
* @param lessonId the generated lesson id path variable
* @param questionId the generated question id path variable
* @param lessonQuestionId the generated lesson question id path variable
* @param answerModel the completed answer model form
* @return redirects to the lesson template with the lesson id
*/
@PostMapping(value = "/lesson/{lessonId}/{questionId}")
public String createAnswer(@PathVariable("lessonId") Integer lessonId, @PathVariable("questionId") Integer questionId, @ModelAttribute AnswerModel answerModel) {
@PostMapping(value = "/lesson/{lessonId}/{lessonQuestionId}")
public String createAnswer(@PathVariable("lessonId") Integer lessonId, @PathVariable("lessonQuestionId") Integer lessonQuestionId, @ModelAttribute AnswerModel answerModel) {
logger.info("~~~~~ Creating answer");
// pull lesson model for lesson id and lesson question id
QuestionModel questionModel = questionService.getQuestionByLessonQuestionIds(lessonId, lessonQuestionId);
// generate a answer
QuestionModel questionModel = questionService.get(questionId);
answerModel.setQuestionId(questionModel.getQuestionId());
answerModel.setCorrect(answerService.isCorrect(questionModel, answerModel));
answerModel.setDateTime(LocalDateTime.now());
answerModel.setDateTimeHash(utilityService.MD5DateTimeHash(answerModel.getDateTime()));
// check if answer already exists for the question id
if (answerService.existsByQuestionId(questionModel.getQuestionId())) {
// if ot does assign answer id and overwrite existing result
answerModel.setAnswerId(answerService.findByQuestionId(questionModel.getQuestionId()).getAnswerId());
}
answerService.save(answerModel);
// redirect to view
String path = String.valueOf(lessonId);
String view = "/lesson/" + path;
logger.info(view);
logger.info(answerModel.getAnswerPayload());
return "redirect:" + view;
// check NQuestions for lessonId against database
Integer nQuestions = lessonService.get(lessonId).getNQuestions();
Integer nLessonQuestions = questionService.findByLessonId(lessonId).size();
// if the lesson question id is greater than the number of available questions, and also less than the total required questions
if (lessonQuestionId >= nLessonQuestions && lessonQuestionId < nQuestions) {
logger.info("~~~~~ Redirecting to new question.");
// redirect to new lesson question view
String path = String.valueOf(lessonId);
String view = "/newLessonQuestion/" + path;
logger.info(view);
logger.info(answerModel.getAnswerPayload());
return "redirect:" + view;
// otherwise if the lesson question id is less than the number of available question, and also less the total required questions
} else if (lessonQuestionId < nQuestions && lessonQuestionId < nQuestions) {
logger.info("~~~~~ Redirecting to next question.");
// redirect to the the new question in the lesson view
Integer nextLessonQuestionId = lessonQuestionId + 1;
QuestionModel nextQuestionModel = questionService.getQuestionByLessonQuestionIds(lessonId, nextLessonQuestionId);
// redirect to view
String path = String.valueOf(lessonId) + "/" + String.valueOf(lessonQuestionId + 1);
String view = "/lesson/" + path;
logger.info(view);
logger.info(nextQuestionModel.getQuestionPayload());
return "redirect:" + view;
// otherwise all questions have been completed and redirect to results view
} else {
logger.info("~~~~~ Creating result.");
// define decimal formatter
DecimalFormat decimalFormatter = new DecimalFormat("#.##");
decimalFormatter.setRoundingMode(RoundingMode.HALF_EVEN);
// generate the results of the lesson
ArrayList<AnswerModel> lessonAnswers = answerService.findByLessonId(lessonId);
Integer nCorrect = resultService.countTotalCorrect(lessonAnswers);
Float score = Float.valueOf(decimalFormatter.format(Float.valueOf(nCorrect) / Float.valueOf(nQuestions) * 100));
// create a result
ResultModel resultModel = resultService.create(lessonId, nCorrect, score);
// check if an existing results model already exists for the lesson id
if (resultService.existsByLessonId(lessonId)) {
// overwrite the result id with the result id from the result model corresponding to the existing lesson id
resultModel.setResultId(resultService.findByLessonId(lessonId).getResultId());
}
resultService.save(resultModel);
// redirect to view
String path = String.valueOf(lessonId);
String view = "/result/" + path;
logger.info(view);
logger.info(resultModel.getResultPayload());
return "redirect:" + view;
}
}

/**
Expand All @@ -270,11 +298,21 @@ public String getResultPage(@PathVariable("lessonId") Integer lessonId, Model mo
LessonModel lessonModel = lessonService.get(lessonId);
// create results messages
ResultModel resultModel = resultService.findByLessonId(lessonId);
String scoreMessage = "Score: " + String.valueOf(resultModel.getScore()) + "%";
String nCorrectMessage = "Answered " + String.valueOf(resultModel.getNCorrect()) + " out of " + String.valueOf(nQuestions) + " questions correctly";
String scoreMessage = "This lesson is incomplete.";
String nCorrectMessage = "Complete using 'Retry Lesson' below.";
if (resultService.existsByLessonId(lessonId)) {
scoreMessage = "Score: " + String.valueOf(resultModel.getScore()) + "%";
nCorrectMessage = "Answered " + String.valueOf(resultModel.getNCorrect()) + " out of " + String.valueOf(nQuestions) + " questions correctly";
}
String path = String.valueOf(lessonId);
// generate combined lesson questions and answers
ArrayList<LessonQuestionAnswer> lessonQuestionsAnswers = lessonQuestionAnswerService.createLessonSummary(lessonModel);
// map solution to null if answers have not been provided
for (LessonQuestionAnswer lessonQuestionAnswer : lessonQuestionsAnswers) {
if (lessonQuestionAnswer.getAnswerId() == null) {
lessonQuestionAnswer.setSolution(null);
}
}
// add attributes to model object
model.addAttribute("scoreMessage", scoreMessage);
model.addAttribute("nCorrectMessage", nCorrectMessage);
Expand All @@ -284,19 +322,6 @@ public String getResultPage(@PathVariable("lessonId") Integer lessonId, Model mo
return "result";
}

/**
* <p>
* Posts user input from the result template page</p>
*
* @param lessonId the generated lesson id path variable
* @return redirects to the home template
*/
@PostMapping(value = "/resultHome/{lessonId}")
public String redirectResulttoHome(@PathVariable("lessonId") Integer lessonId) {
logger.info("~~~~~ Redirecting home.");
return "redirect:/home";
}

/**
* <p>
* Posts user input from the result template page</p>
Expand All @@ -305,9 +330,9 @@ public String redirectResulttoHome(@PathVariable("lessonId") Integer lessonId) {
* @return redirects to the lesson template with the same lesson
* configurations
*/
@PostMapping(value = "/resultRedo/{lessonId}")
public String redirectResulttoLesson(@PathVariable("lessonId") Integer lessonId) {
logger.info("~~~~~ Creating lesson");
@PostMapping(value = "/resultNew/{lessonId}")
public String redirectResultToNewLesson(@PathVariable("lessonId") Integer lessonId) {
logger.info("~~~~~ Creating new lesson");
// generate a new lesson from the current lesson
LessonModel currentLessonModel = lessonService.get(lessonId);
LessonModel newLessonModel = new LessonModel();
Expand All @@ -321,9 +346,29 @@ public String redirectResulttoLesson(@PathVariable("lessonId") Integer lessonId)
lessonService.save(newLessonModel);
// redirect to view
String path = String.valueOf(newLessonModel.getLessonId());
String view = "/lesson/" + path;
String view = "/newLessonQuestion/" + path;
logger.info(newLessonModel.getLessonPayload());
return "redirect:" + view;
}

/**
* <p>
* Posts user input from the result template page</p>
*
* @param lessonId the generated lesson id path variable
* @return redirects to the lesson template with the same lesson
* configurations
*/
@PostMapping(value = "/resultRetry/{lessonId}")
public String redirectResultToRetryLesson(@PathVariable("lessonId") Integer lessonId) {
logger.info("~~~~~ Retrying lesson");
// generate a new lesson from the current lesson
LessonModel currentLessonModel = lessonService.get(lessonId);
// redirect to view
String path = String.valueOf(lessonId) + "/1";
String view = "/lesson/" + path;
logger.info(currentLessonModel.getLessonPayload());
return "redirect:" + view;
}

}
Loading