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:
parent
56e1e42d65
commit
2f82568f46
6 changed files with 124 additions and 12 deletions
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() {
|
||||||
|
|
24
src/main/java/es/kauron/jstudy/model/AnsweredItem.java
Normal file
24
src/main/java/es/kauron/jstudy/model/AnsweredItem.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())) {
|
||||||
|
|
21
src/main/resources/es/kauron/jstudy/view/stats.fxml
Normal file
21
src/main/resources/es/kauron/jstudy/view/stats.fxml
Normal 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>
|
Loading…
Reference in a new issue