1
0
Fork 0
mirror of https://gitlab.com/kauron/jstudy synced 2024-12-22 08:23:33 +01:00

Test: added stats (time, answer and q/a) after test.

This commit is contained in:
Carlos Galindo 2019-09-13 18:16:53 +02:00
parent 56e1e42d65
commit 2f82568f46
Signed by: kauron
GPG key ID: 83E68706DEE119A3
6 changed files with 124 additions and 12 deletions

View file

@ -1,6 +1,7 @@
package es.kauron.jstudy.controller; package es.kauron.jstudy.controller;
import es.kauron.jstudy.Main; import es.kauron.jstudy.Main;
import es.kauron.jstudy.model.AnsweredItem;
import es.kauron.jstudy.model.AppPrefs; import es.kauron.jstudy.model.AppPrefs;
import es.kauron.jstudy.model.TestItem; import es.kauron.jstudy.model.TestItem;
import javafx.application.Platform; import javafx.application.Platform;
@ -356,7 +357,7 @@ public class Controller implements Initializable {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("view/test.fxml")); FXMLLoader loader = new FXMLLoader(Main.class.getResource("view/test.fxml"));
Parent root = loader.load(); Parent root = loader.load();
((TestController) loader.getController()).setList(new ArrayList<>(list)); ((TestController) loader.getController()).setData(new ArrayList<>(list), this);
theTest = new Tab("Test: " + tabPane.getSelectionModel().getSelectedItem().getText(), root); theTest = new Tab("Test: " + tabPane.getSelectionModel().getSelectedItem().getText(), root);
tabPane.getTabs().add(theTest); tabPane.getTabs().add(theTest);
@ -367,6 +368,20 @@ public class Controller implements Initializable {
} }
} }
void createStatsTab(List<AnsweredItem> answers) {
try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("view/stats.fxml"));
Parent root = loader.load();
((StatsController) loader.getController()).setData(answers);
Matcher m = Pattern.compile("^Test: (.*)$").matcher(tabPane.getSelectionModel().getSelectedItem().getText());
m.find();
tabPane.getTabs().add(new Tab("Stats: " + m.group(1), root));
tabPane.getSelectionModel().selectLast();
} catch (IOException e) {
e.printStackTrace();
}
}
@FXML @FXML
protected void onAboutAction(ActionEvent event) { protected void onAboutAction(ActionEvent event) {
if (Desktop.isDesktopSupported()) { if (Desktop.isDesktopSupported()) {

View file

@ -0,0 +1,42 @@
package es.kauron.jstudy.controller;
import es.kauron.jstudy.model.AnsweredItem;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
public class StatsController implements Initializable {
@FXML
protected TableView<AnsweredItem> table;
@FXML
protected TableColumn<AnsweredItem, String> numCol, questionCol, answerCol, userAnswerCol, timeCol;
@Override
public void initialize(URL location, ResourceBundle resources) {
numCol.setCellValueFactory(e -> e.getValue().indexProperty.asString());
questionCol.setCellValueFactory(e -> e.getValue().item.questionProperty());
answerCol.setCellValueFactory(e -> e.getValue().item.answerProperty());
userAnswerCol.setCellValueFactory(e -> e.getValue().answerProperty);
timeCol.setCellValueFactory(e -> Bindings.format("%.3f", e.getValue().timeProperty.divide(1e9)));
}
public void setData(List<AnsweredItem> answers) {
table.setItems(FXCollections.observableArrayList(answers));
// Set wrong answers in bold
table.setRowFactory(param -> {
TableRow<AnsweredItem> row = new TableRow<>();
row.itemProperty().addListener((obj, o, n) ->
row.setStyle(n == null || n.isRight() ? "" : "-fx-font-weight: bold"));
return row;
});
}
}

View file

@ -1,5 +1,6 @@
package es.kauron.jstudy.controller; package es.kauron.jstudy.controller;
import es.kauron.jstudy.model.AnsweredItem;
import es.kauron.jstudy.model.AppPrefs; import es.kauron.jstudy.model.AppPrefs;
import es.kauron.jstudy.model.TestItem; import es.kauron.jstudy.model.TestItem;
import es.kauron.jstudy.util.Clock; import es.kauron.jstudy.util.Clock;
@ -12,6 +13,7 @@ import javafx.scene.control.*;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -36,6 +38,9 @@ public class TestController implements Initializable {
private final IntegerProperty errors = new SimpleIntegerProperty(0); private final IntegerProperty errors = new SimpleIntegerProperty(0);
private final ObjectProperty<TestItem> item = new SimpleObjectProperty<>(); private final ObjectProperty<TestItem> item = new SimpleObjectProperty<>();
private final IntegerProperty done = new SimpleIntegerProperty(0); private final IntegerProperty done = new SimpleIntegerProperty(0);
private final List<AnsweredItem> answers = new ArrayList<>();
private long timeQuestionStarted = 0;
private Controller controller;
// Time accounting // Time accounting
private final Clock clock = new Clock(); private final Clock clock = new Clock();
@ -51,7 +56,8 @@ public class TestController implements Initializable {
}); });
} }
void setList(List<TestItem> list) { void setData(List<TestItem> list, Controller controller) {
this.controller = controller;
this.list = list; this.list = list;
int total = list.size(); int total = list.size();
progressLabel.textProperty().bind(Bindings.format( progressLabel.textProperty().bind(Bindings.format(
@ -72,13 +78,18 @@ public class TestController implements Initializable {
@FXML @FXML
private void onNextAction(ActionEvent event) { private void onNextAction(ActionEvent event) {
// Do not accept empty responses
if (answer.getText().trim().isEmpty()) return;
// Record the answer
long timeElapsed = System.nanoTime() - timeQuestionStarted;
AnsweredItem ai = new AnsweredItem(item.get(), answers.size() + 1, answer.getText().trim(), timeElapsed);
answers.add(ai);
prevAnswer.setText(answer.getText()); prevAnswer.setText(answer.getText());
boolean right = item.get().checkAnswer(answer.getText()); correctAnswer.setVisible(!ai.isRight());
correctAnswer.setVisible(!right); correctLabel.setVisible(!ai.isRight());
correctLabel.setVisible(!right);
if (!right) { if (!ai.isRight()) {
errors.set(errors.get() + 1); errors.set(errors.get() + 1);
prevAnswer.setStyle("-fx-text-fill: #C40000;"); prevAnswer.setStyle("-fx-text-fill: #C40000;");
} else { } else {
@ -86,10 +97,10 @@ public class TestController implements Initializable {
} }
// Remove the question from the pool if the question was correctly answered or there is no repetition // Remove the question from the pool if the question was correctly answered or there is no repetition
if (right || !AppPrefs.repeatWrong.get()) { if (ai.isRight() || !AppPrefs.repeatWrong.get()) {
if (!correctingError.get()) if (!correctingError.get())
done.set(done.get() + 1); done.set(done.get() + 1);
correctingError.set(!right && AppPrefs.repeatImmediately.get()); correctingError.set(!ai.isRight() && AppPrefs.repeatImmediately.get());
list.remove(item.get()); list.remove(item.get());
if (list.size() == 0) { if (list.size() == 0) {
onEndAction(null); onEndAction(null);
@ -105,6 +116,7 @@ public class TestController implements Initializable {
} }
answer.setText(""); answer.setText("");
answer.requestFocus(); answer.requestFocus();
timeQuestionStarted = System.nanoTime();
} }
private void chooseQuestion() { private void chooseQuestion() {
@ -121,6 +133,7 @@ public class TestController implements Initializable {
chooseQuestion(); chooseQuestion();
answer.setText(""); answer.setText("");
answer.requestFocus(); answer.requestFocus();
timeQuestionStarted = System.nanoTime();
} }
@FXML @FXML
@ -130,6 +143,7 @@ public class TestController implements Initializable {
pauseCheckBox.setSelected(true); pauseCheckBox.setSelected(true);
pauseCheckBox.setDisable(true); pauseCheckBox.setDisable(true);
clock.stop(); clock.stop();
controller.createStatsTab(answers);
} }
void stopTimer() { void stopTimer() {

View file

@ -0,0 +1,24 @@
package es.kauron.jstudy.model;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyLongWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
public class AnsweredItem {
public final ReadOnlyStringWrapper answerProperty;
public final ReadOnlyLongWrapper timeProperty;
public final ReadOnlyIntegerProperty indexProperty;
public final TestItem item;
public AnsweredItem(TestItem item, int index, String answer, long timeNano) {
this.item = item;
this.indexProperty = new ReadOnlyIntegerWrapper(index);
this.answerProperty = new ReadOnlyStringWrapper(answer);
this.timeProperty = new ReadOnlyLongWrapper(timeNano);
}
public boolean isRight() {
return item.getAnswer().equals(answerProperty.get());
}
}

View file

@ -49,10 +49,6 @@ public class TestItem {
return !question.get().isEmpty() && !answer.get().isEmpty(); return !question.get().isEmpty() && !answer.get().isEmpty();
} }
public boolean checkAnswer(String answer) {
return getAnswer().equals(answer);
}
public static void saveTo(File file, List<TestItem> data) { public static void saveTo(File file, List<TestItem> data) {
try (OutputStreamWriter writer = new OutputStreamWriter( try (OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream(file), StandardCharsets.UTF_8.newEncoder())) { new FileOutputStream(file), StandardCharsets.UTF_8.newEncoder())) {

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/8.0.202-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="es.kauron.jstudy.controller.StatsController">
<children>
<TableView fx:id="table" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="400.0" HBox.hgrow="ALWAYS" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="numCol" text="#" style="-fx-alignment: CENTER-RIGHT" />
<TableColumn fx:id="questionCol" prefWidth="75.0" text="Question" />
<TableColumn fx:id="answerCol" prefWidth="75.0" text="Correct answer" />
<TableColumn fx:id="userAnswerCol" prefWidth="75.0" text="Your answer" />
<TableColumn fx:id="timeCol" prefWidth="15.0" text="Time" style="-fx-alignment: CENTER-RIGHT" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</VBox>