diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java index 40945ba2a8..a479ebd0b6 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/managers/GameManager.java @@ -149,7 +149,7 @@ public GameManager(final GitContentManager contentManager, * list of question categories (i.e. problem_solving, book) to include in filtered results * @param boardOwner * The user that should be marked as the creator of the gameBoard. - * @return a gameboard if possible that satisifies the conditions provided by the parameters. Will return null if no + * @return a gameboard if possible that satisfies the conditions provided by the parameters. Will return null if no * questions can be provided. * @throws SegueDatabaseException * - if there is an error contacting the database. diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/PgQuizQuestionAttemptPersistenceManager.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/PgQuizQuestionAttemptPersistenceManager.java index af593668d2..1728197de8 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/PgQuizQuestionAttemptPersistenceManager.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dao/PgQuizQuestionAttemptPersistenceManager.java @@ -59,8 +59,8 @@ public PgQuizQuestionAttemptPersistenceManager(final PostgresSqlDb database, fin @Override public void registerQuestionAttempt(Long quizAttemptId, QuestionValidationResponse questionResponse) throws SegueDatabaseException { - String query = "INSERT INTO quiz_question_attempts(quiz_attempt_id, question_id, question_attempt, correct, \"timestamp\")" + - " VALUES (?, ?, ?::text::jsonb, ?, ?);"; + String query = "INSERT INTO quiz_question_attempts(quiz_attempt_id, question_id, question_attempt, correct, \"timestamp\", marks)" + + " VALUES (?, ?, ?::text::jsonb, ?, ?, ?);"; try (Connection conn = database.getDatabaseConnection(); PreparedStatement pst = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); ) { @@ -75,6 +75,12 @@ public void registerQuestionAttempt(Long quizAttemptId, QuestionValidationRespon } pst.setTimestamp(5, new java.sql.Timestamp(questionResponse.getDateAttempted().getTime())); + if (questionResponse.getMarks() != null) { + pst.setInt(6, questionResponse.getMarks()); + } else { + pst.setInt(6, java.sql.Types.NULL); + } + if (pst.executeUpdate() == 0) { throw new SegueDatabaseException("Unable to save quiz question attempt."); } diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LLMFreeTextQuestionValidationResponse.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LLMFreeTextQuestionValidationResponse.java index f408ef2a76..87caa526b6 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LLMFreeTextQuestionValidationResponse.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LLMFreeTextQuestionValidationResponse.java @@ -11,7 +11,6 @@ @DTOMapping(LLMFreeTextQuestionValidationResponseDTO.class) public class LLMFreeTextQuestionValidationResponse extends QuestionValidationResponse { - private Integer marksAwarded; private List markBreakdown; public LLMFreeTextQuestionValidationResponse() { @@ -24,10 +23,10 @@ public LLMFreeTextQuestionValidationResponse(final String questionId, final Choi } public Integer getMarksAwarded() { - return marksAwarded; + return super.getMarks(); } public void setMarksAwarded(Integer marksAwarded) { - this.marksAwarded = marksAwarded; + super.setMarks(marksAwarded); } public List getMarkBreakdown() { diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LightweightQuestionValidationResponse.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LightweightQuestionValidationResponse.java index fab7a7ef32..223b93df85 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LightweightQuestionValidationResponse.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/LightweightQuestionValidationResponse.java @@ -10,6 +10,7 @@ public class LightweightQuestionValidationResponse { private String questionId; private Boolean correct; private Date dateAttempted; + private Integer marks; /** * Default Constructor for mappers. @@ -27,12 +28,15 @@ public LightweightQuestionValidationResponse() { * - * @param dateAttempted * - + * @param marks + * - */ public LightweightQuestionValidationResponse(final String questionId, final Boolean correct, - final Date dateAttempted) { + final Date dateAttempted, final Integer marks) { this.questionId = questionId; this.correct = correct; this.dateAttempted = dateAttempted; + this.marks = marks; } /** @@ -92,9 +96,28 @@ public void setDateAttempted(final Date dateAttempted) { this.dateAttempted = dateAttempted; } + /** + * Gets the marks. + * + * @return the marks + */ + public Integer getMarks() { + return marks; + } + + /** + * Sets the marks. + * + * @param marks + * the marks to set + */ + public void setMarks(final Integer marks) { + this.marks = marks; + } + @Override public String toString() { return "QuestionValidationResponse [questionId=" + questionId + ", correct=" + correct + - ", dateAttempted=" + dateAttempted + "]"; + ", dateAttempted=" + dateAttempted + ", marks=" + marks + "]"; } } diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/QuestionValidationResponse.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/QuestionValidationResponse.java index 916ec005c5..8325a6ea2b 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/QuestionValidationResponse.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dos/QuestionValidationResponse.java @@ -51,10 +51,33 @@ public QuestionValidationResponse() { * - * @param dateAttempted * - + * @param marks + * - + */ + public QuestionValidationResponse(final String questionId, final Choice answer, final Boolean correct, + final Content explanation, final Date dateAttempted, final Integer marks) { + super(questionId, correct, dateAttempted, marks); + this.answer = answer; + this.explanation = explanation; + } + + /** + * Constructor without specifying marks (instead derived from 'correct') + * + * @param questionId + * - + * @param answer + * - + * @param correct + * - + * @param explanation + * - + * @param dateAttempted + * - */ public QuestionValidationResponse(final String questionId, final Choice answer, final Boolean correct, final Content explanation, final Date dateAttempted) { - super(questionId, correct, dateAttempted); + super(questionId, correct, dateAttempted, (correct != null && correct) ? 1 : 0); this.answer = answer; this.explanation = explanation; } @@ -101,7 +124,7 @@ public final void setExplanation(final Content explanation) { public String toString() { return "QuestionValidationResponse [questionId=" + super.getQuestionId() + ", answer=" + answer + ", correct=" + super.isCorrect() + ", explanation=" + explanation + - ", dateAttempted=" + super.getDateAttempted() + "]"; + ", dateAttempted=" + super.getDateAttempted() + ", marks=" + super.getMarks() + "]"; } } diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/FormulaValidationResponseDTO.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/FormulaValidationResponseDTO.java index 5bd5b4c2ec..c685e95385 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/FormulaValidationResponseDTO.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/FormulaValidationResponseDTO.java @@ -52,12 +52,14 @@ public FormulaValidationResponseDTO() { * - * @param dateAttempted * - + * @param marks + * - */ public FormulaValidationResponseDTO(final String questionId, final ChoiceDTO answer, final ContentDTO explanation, final Boolean correctExact, final Boolean correctSymbolic, final Boolean correctNumeric, - final Date dateAttempted) { - super(questionId, answer, correctSymbolic || correctNumeric, explanation, dateAttempted); + final Date dateAttempted, final Integer marks) { + super(questionId, answer, correctSymbolic || correctNumeric, explanation, dateAttempted, marks); this.correctExact = correctExact; this.correctSymbolic = correctSymbolic; this.correctNumeric = correctNumeric; diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/ItemValidationResponseDTO.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/ItemValidationResponseDTO.java index f4bfb3bab9..d39f35a8ab 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/ItemValidationResponseDTO.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/ItemValidationResponseDTO.java @@ -46,11 +46,12 @@ public ItemValidationResponseDTO() { * @param itemsCorrect - ordered list of correctness status of each submitted item. * @param explanation - explanation. * @param dateAttempted - dateAttempted. + * @param marks - marks */ public ItemValidationResponseDTO(final String questionId, final ChoiceDTO answer, final Boolean correct, final List itemsCorrect, - final ContentDTO explanation, final Date dateAttempted) { - super(questionId, answer, correct, explanation, dateAttempted); + final ContentDTO explanation, final Date dateAttempted, final Integer marks) { + super(questionId, answer, correct, explanation, dateAttempted, marks); this.itemsCorrect = itemsCorrect; } diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/LLMFreeTextQuestionValidationResponseDTO.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/LLMFreeTextQuestionValidationResponseDTO.java index 8baa85aaa1..f23652cd7f 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/LLMFreeTextQuestionValidationResponseDTO.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/LLMFreeTextQuestionValidationResponseDTO.java @@ -5,17 +5,16 @@ import java.util.List; public class LLMFreeTextQuestionValidationResponseDTO extends QuestionValidationResponseDTO { - private Integer marksAwarded; private List markBreakdown; public LLMFreeTextQuestionValidationResponseDTO() { } public Integer getMarksAwarded() { - return marksAwarded; + return super.getMarks(); } public void setMarksAwarded(Integer marksAwarded) { - this.marksAwarded = marksAwarded; + super.setMarks(marksAwarded); } public List getMarkBreakdown() { diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuantityValidationResponseDTO.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuantityValidationResponseDTO.java index 0b9b4af85e..8e49389a8f 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuantityValidationResponseDTO.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuantityValidationResponseDTO.java @@ -53,11 +53,13 @@ public QuantityValidationResponseDTO() { * - * @param dateAttempted * - + * @param marks + * - */ public QuantityValidationResponseDTO(final String questionId, final ChoiceDTO answer, final Boolean correct, final ContentDTO explanation, final Boolean correctValue, - final Boolean correctUnits, final Date dateAttempted) { - super(questionId, answer, correct, explanation, dateAttempted); + final Boolean correctUnits, final Date dateAttempted, final Integer marks) { + super(questionId, answer, correct, explanation, dateAttempted, marks); this.correctValue = correctValue; this.correctUnits = correctUnits; } diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuestionValidationResponseDTO.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuestionValidationResponseDTO.java index 4b06bfdaa6..3c92f8cb4a 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuestionValidationResponseDTO.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/dto/QuestionValidationResponseDTO.java @@ -30,6 +30,7 @@ public class QuestionValidationResponseDTO { private Boolean correct; private ContentDTO explanation; private Date dateAttempted; + private Integer marks; /** * Default Constructor for mappers. @@ -51,14 +52,17 @@ public QuestionValidationResponseDTO() { * - * @param dateAttempted * - + * @param marks + * - */ public QuestionValidationResponseDTO(final String questionId, final ChoiceDTO answer, final Boolean correct, - final ContentDTO explanation, final Date dateAttempted) { + final ContentDTO explanation, final Date dateAttempted, final Integer marks) { this.questionId = questionId; this.answer = answer; this.correct = correct; this.explanation = explanation; this.dateAttempted = dateAttempted; + this.marks = marks; } /** @@ -156,9 +160,28 @@ public void setDateAttempted(final Date dateAttempted) { this.dateAttempted = dateAttempted; } + /** + * Gets the marks. + * + * @return the marks + */ + public Integer getMarks() { + return marks; + } + + /** + * Sets the marks. + * + * @param marks + * the marks to set + */ + public void setMarks(final Integer marks) { + this.marks = marks; + } + @Override public String toString() { return "QuestionValidationResponseDTO [questionId=" + questionId + ", answer=" + answer + ", correct=" - + correct + ", explanation=" + explanation + ", dateAttempted=" + dateAttempted + "]"; + + correct + ", explanation=" + explanation + ", dateAttempted=" + dateAttempted + ", marks=" + marks + "]"; } } diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidator.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidator.java index a811eec313..7384c12805 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidator.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidator.java @@ -276,12 +276,13 @@ private int evaluateMarkTotal(final IsaacLLMFreeTextQuestion question, final Map * @param question the question being marked so that we can return the mark scheme. * @param answer the user's attempt at the question. * @param awardedMarks the marks awarded for each field in the mark scheme according to the LLM response. + * @param markTotal the calculated mark value based on which individual marks were awarded * @return a response to the user's attempt at the question. */ private LLMFreeTextQuestionValidationResponse generateQuestionValidationResponse( final IsaacLLMFreeTextQuestion question, final Choice answer, final Map awardedMarks, final int markTotal) { - boolean isConsideredCorrect = markTotal > 0; + boolean isConsideredCorrect = markTotal >= question.getMaxMarks(); // We create a fresh copy of the mark scheme with the full description and the awarded mark values. List markBreakdown = question.getMarkScheme().stream().map(mark -> { diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/PgQuestionAttempts.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/PgQuestionAttempts.java index f48ad5ef73..958107ae53 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/PgQuestionAttempts.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/quiz/PgQuestionAttempts.java @@ -186,8 +186,8 @@ public Map>> getAnonymousQu public void registerQuestionAttempt(final Long userId, final String questionPageId, final String fullQuestionId, final QuestionValidationResponse questionAttempt) throws SegueDatabaseException { - String query = "INSERT INTO question_attempts(user_id, page_id, question_id, question_attempt, correct, \"timestamp\")" - + " VALUES (?, ?, ?, ?::text::jsonb, ?, ?);"; + String query = "INSERT INTO question_attempts(user_id, page_id, question_id, question_attempt, correct, \"timestamp\", marks)" + + " VALUES (?, ?, ?, ?::text::jsonb, ?, ?, ?);"; try (Connection conn = database.getDatabaseConnection(); PreparedStatement pst = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); ) { @@ -201,8 +201,15 @@ public void registerQuestionAttempt(final Long userId, final String questionPage } else { pst.setNull(5, java.sql.Types.NULL); } + pst.setTimestamp(6, new java.sql.Timestamp(questionAttempt.getDateAttempted().getTime())); + if (questionAttempt.getMarks() != null) { + pst.setInt(7, questionAttempt.getMarks()); + } else { + pst.setInt(7, java.sql.Types.NULL); + } + if (pst.executeUpdate() == 0) { throw new SegueDatabaseException("Unable to save question attempt."); } @@ -260,7 +267,7 @@ public Map>>> mapToReturn @@ -307,7 +314,7 @@ public Map>> get = userIds.stream().collect(Collectors.toMap(Function.identity(), k -> Maps.newHashMap()));; try (Connection conn = database.getDatabaseConnection()) { - String query = "SELECT id, user_id, question_id, correct, timestamp FROM question_attempts" + String query = "SELECT id, user_id, question_id, correct, timestamp, marks FROM question_attempts" + " WHERE user_id = ANY(?) AND page_id = ANY(?)" + " ORDER BY \"timestamp\" ASC"; @@ -438,9 +445,10 @@ public Map getQuestionAttemptCountForUserByDateRange(final Date from private LightweightQuestionValidationResponse resultsToLightweightValidationResponse(final ResultSet results) throws SQLException { LightweightQuestionValidationResponse partialQuestionAttempt = new QuestionValidationResponse(); - partialQuestionAttempt.setCorrect(results.getBoolean("correct")); + partialQuestionAttempt.setCorrect(results.getInt("marks") > 0); partialQuestionAttempt.setQuestionId(results.getString("question_id")); partialQuestionAttempt.setDateAttempted(results.getTimestamp("timestamp")); + partialQuestionAttempt.setMarks(results.getInt("marks")); return partialQuestionAttempt; } diff --git a/src/main/resources/db_scripts/create_anonymous_database.sql b/src/main/resources/db_scripts/create_anonymous_database.sql index b95b101599..eaa4c0d7af 100644 --- a/src/main/resources/db_scripts/create_anonymous_database.sql +++ b/src/main/resources/db_scripts/create_anonymous_database.sql @@ -151,7 +151,8 @@ CREATE TABLE anonymous.question_attempts AS question_id, question_attempt, correct, - timestamp + timestamp, + marks FROM public.question_attempts; CREATE TABLE anonymous.user_streak_freezes AS @@ -200,7 +201,8 @@ CREATE TABLE anonymous.quiz_question_attempts AS question_id, question_attempt, correct, - timestamp + timestamp, + marks FROM public.quiz_question_attempts; -- Logged events: diff --git a/src/main/resources/db_scripts/migrations/2025-10-questions-attempts-add-mark-field.sql b/src/main/resources/db_scripts/migrations/2025-10-questions-attempts-add-mark-field.sql new file mode 100644 index 0000000000..d7886066d6 --- /dev/null +++ b/src/main/resources/db_scripts/migrations/2025-10-questions-attempts-add-mark-field.sql @@ -0,0 +1,99 @@ +ALTER TABLE question_attempts +ADD marks integer; + +ALTER TABLE quiz_question_attempts +ADD marks integer; + +CREATE OR REPLACE FUNCTION update_marks_for_period(qa question_attempts) + RETURNS int AS $$ +BEGIN + RETURN CASE + WHEN (qa.question_attempt -> 'answer' ->> 'type') = 'llmFreeTextChoice' + THEN (qa.question_attempt ->> 'marksAwarded')::int + ELSE qa.correct::int + END; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION quiz_update_marks_for_period(qa quiz_question_attempts) + RETURNS int AS $$ +BEGIN + RETURN CASE + WHEN (qa.question_attempt -> 'answer' ->> 'type') = 'llmFreeTextChoice' + THEN (qa.question_attempt ->> 'marksAwarded')::int + ELSE qa.correct::int + END; +END; +$$ LANGUAGE plpgsql; + +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2014-01-01' AND timestamp <= '2015-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2015-01-01' AND timestamp <= '2016-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2016-01-01' AND timestamp <= '2016-06-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2016-06-01' AND timestamp <= '2017-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2017-01-01' AND timestamp <= '2017-06-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2017-06-01' AND timestamp <= '2018-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2018-01-01' AND timestamp <= '2018-06-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2018-06-01' AND timestamp <= '2018-10-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2018-10-01' AND timestamp <= '2019-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2019-01-01' AND timestamp <= '2019-06-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2019-06-01' AND timestamp <= '2019-10-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2019-10-01' AND timestamp <= '2020-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2020-01-01' AND timestamp <= '2020-04-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2020-04-01' AND timestamp <= '2020-09-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2020-09-01' AND timestamp <= '2020-10-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2020-10-01' AND timestamp <= '2021-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2021-01-01' AND timestamp <= '2021-04-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2021-04-01' AND timestamp <= '2021-09-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2021-09-01' AND timestamp <= '2021-10-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2021-10-01' AND timestamp <= '2022-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2022-01-01' AND timestamp <= '2022-04-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2022-04-01' AND timestamp <= '2022-09-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2022-09-01' AND timestamp <= '2022-10-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2022-10-01' AND timestamp <= '2023-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2023-01-01' AND timestamp <= '2023-03-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2023-03-01' AND timestamp <= '2023-06-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2023-06-01' AND timestamp <= '2023-09-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2023-09-01' AND timestamp <= '2023-10-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2023-10-01' AND timestamp <= '2023-11-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2023-11-01' AND timestamp <= '2024-01-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2024-01-01' AND timestamp <= '2024-02-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2024-02-01' AND timestamp <= '2024-03-02'; +UPDATE question_attempts SET marks = update_marks_for_period(question_attempts) WHERE timestamp > '2024-03-01' AND timestamp <= '2026-01-02'; + + +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2014-01-01' AND timestamp <= '2015-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2015-01-01' AND timestamp <= '2016-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2016-01-01' AND timestamp <= '2016-06-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2016-06-01' AND timestamp <= '2017-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2017-01-01' AND timestamp <= '2017-06-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2017-06-01' AND timestamp <= '2018-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2018-01-01' AND timestamp <= '2018-06-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2018-06-01' AND timestamp <= '2018-10-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2018-10-01' AND timestamp <= '2019-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2019-01-01' AND timestamp <= '2019-06-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2019-06-01' AND timestamp <= '2019-10-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2019-10-01' AND timestamp <= '2020-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2020-01-01' AND timestamp <= '2020-04-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2020-04-01' AND timestamp <= '2020-09-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2020-09-01' AND timestamp <= '2020-10-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2020-10-01' AND timestamp <= '2021-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2021-01-01' AND timestamp <= '2021-04-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2021-04-01' AND timestamp <= '2021-09-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2021-09-01' AND timestamp <= '2021-10-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2021-10-01' AND timestamp <= '2022-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2022-01-01' AND timestamp <= '2022-04-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2022-04-01' AND timestamp <= '2022-09-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2022-09-01' AND timestamp <= '2022-10-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2022-10-01' AND timestamp <= '2023-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2023-01-01' AND timestamp <= '2023-03-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2023-03-01' AND timestamp <= '2023-06-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2023-06-01' AND timestamp <= '2023-09-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2023-09-01' AND timestamp <= '2023-10-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2023-10-01' AND timestamp <= '2023-11-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2023-11-01' AND timestamp <= '2024-01-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2024-01-01' AND timestamp <= '2024-02-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2024-02-01' AND timestamp <= '2024-03-02'; +UPDATE quiz_question_attempts SET marks = quiz_update_marks_for_period(quiz_question_attempts) WHERE timestamp > '2024-03-01' AND timestamp <= '2026-01-02'; + +DROP FUNCTION update_marks_for_period; +DROP FUNCTION quiz_update_marks_for_period; \ No newline at end of file diff --git a/src/main/resources/db_scripts/postgres-rutherford-create-script.sql b/src/main/resources/db_scripts/postgres-rutherford-create-script.sql index 0647661efb..3f976ce9ad 100644 --- a/src/main/resources/db_scripts/postgres-rutherford-create-script.sql +++ b/src/main/resources/db_scripts/postgres-rutherford-create-script.sql @@ -346,7 +346,8 @@ CREATE TABLE public.question_attempts ( question_id text NOT NULL, question_attempt jsonb, correct boolean, - "timestamp" timestamp without time zone + "timestamp" timestamp without time zone, + marks integer ); @@ -460,7 +461,8 @@ CREATE TABLE public.quiz_question_attempts ( question_id text NOT NULL, question_attempt jsonb, correct boolean, - "timestamp" timestamp without time zone + "timestamp" timestamp without time zone, + marks integer ); diff --git a/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/QuizQuestionManagerTest.java b/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/QuizQuestionManagerTest.java index c524a7c421..6409a1e424 100644 --- a/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/QuizQuestionManagerTest.java +++ b/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/QuizQuestionManagerTest.java @@ -111,8 +111,8 @@ public void initializeAdditionalObjects() { correctResponse = new QuestionValidationResponse(question.getId(), correctAnswer, true, correctExplanation, somePastDate); wrongResponse = new QuestionValidationResponse(question.getId(), wrongAnswer, false, wrongExplanation, somePastDate); - correctResponseDTO = new QuestionValidationResponseDTO(question.getId(), correctAnswerDTO, true, correctExplanationDTO, somePastDate); - wrongResponseDTO = new QuestionValidationResponseDTO(question.getId(), wrongAnswerDTO, false, wrongExplanationDTO, somePastDate); + correctResponseDTO = new QuestionValidationResponseDTO(question.getId(), correctAnswerDTO, true, correctExplanationDTO, somePastDate, 1); + wrongResponseDTO = new QuestionValidationResponseDTO(question.getId(), wrongAnswerDTO, false, wrongExplanationDTO, somePastDate, 0); quantityResponse = new QuantityValidationResponse(question.getId(), correctAnswer, true, correctExplanation, true, true, somePastDate); diff --git a/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/UserAttemptManagerTest.java b/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/UserAttemptManagerTest.java index bcaf2a1e81..8ec693a108 100644 --- a/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/UserAttemptManagerTest.java +++ b/src/test/java/uk/ac/cam/cl/dtg/isaac/api/managers/UserAttemptManagerTest.java @@ -29,14 +29,14 @@ public class UserAttemptManagerTest { private ContentSummaryDTO fakeQuestionSummary; private ContentSummaryDTO fakeConceptSummary; - private final LightweightQuestionValidationResponse p1CorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_1_ID, true, null); - private final LightweightQuestionValidationResponse p1IncorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_1_ID, false, null); - private final LightweightQuestionValidationResponse p2CorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_2_ID, true, null); - private final LightweightQuestionValidationResponse p2IncorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_2_ID, false, null); - private final LightweightQuestionValidationResponse p3CorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_3_ID, true, null); - private final LightweightQuestionValidationResponse p3IncorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_3_ID, false, null); - - private final LightweightQuestionValidationResponse someOtherCorrectAttempt = new LightweightQuestionValidationResponse("some-other-part-id", true, null); + private final LightweightQuestionValidationResponse p1CorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_1_ID, true, null, 1); + private final LightweightQuestionValidationResponse p1IncorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_1_ID, false, null, 0); + private final LightweightQuestionValidationResponse p2CorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_2_ID, true, null, 1); + private final LightweightQuestionValidationResponse p2IncorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_2_ID, false, null, 0); + private final LightweightQuestionValidationResponse p3CorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_3_ID, true, null, 1); + private final LightweightQuestionValidationResponse p3IncorrectAttempt = new LightweightQuestionValidationResponse(QUESTION_PART_3_ID, false, null, 0); + + private final LightweightQuestionValidationResponse someOtherCorrectAttempt = new LightweightQuestionValidationResponse("some-other-part-id", true, null, 1); @Before public void setUp() throws Exception { diff --git a/src/test/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidatorTest.java b/src/test/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidatorTest.java index 40756488ae..2ebac5124b 100644 --- a/src/test/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidatorTest.java +++ b/src/test/java/uk/ac/cam/cl/dtg/isaac/quiz/IsaacLLMFreeTextValidatorTest.java @@ -106,7 +106,7 @@ private static Object[][] genericTwoMarkCases() { {"A two-mark answer for a default marking formula two-mark question gets recognised as correct", question, "{\"reasonFoo\": 1, \"reasonBar\": 1, \"reasonFizz\": 0}", CORRECT, TWO_MARKS}, {"A one-mark answer for a default marking formula two-mark question receives exactly one mark", - question, "{\"reasonFoo\": 1, \"reasonBar\": 0, \"reasonFizz\": 0}", CORRECT, ONE_MARK}}; + question, "{\"reasonFoo\": 1, \"reasonBar\": 0, \"reasonFizz\": 0}", INCORRECT, ONE_MARK}}; } /* @@ -127,9 +127,9 @@ private static Object[][] advantageCases() { {"An answer containing an advantage and a disadvantage mark receives two marks", question, "{\"adv1\": 1, \"adv2\": 0, \"dis1\": 1, \"dis2\": 0}", CORRECT, TWO_MARKS}, {"An answer containing only a disadvantage mark receives one mark", - question, "{\"adv1\": 0, \"adv2\": 0, \"dis1\": 1, \"dis2\": 0}", CORRECT, ONE_MARK}, + question, "{\"adv1\": 0, \"adv2\": 0, \"dis1\": 1, \"dis2\": 0}", INCORRECT, ONE_MARK}, {"An answer containing two advantage marks receives one mark", - question, "{\"adv1\": 1, \"adv2\": 1, \"dis1\": 0, \"dis2\": 0}", CORRECT, ONE_MARK}}; + question, "{\"adv1\": 1, \"adv2\": 1, \"dis1\": 0, \"dis2\": 0}", INCORRECT, ONE_MARK}}; } @@ -156,7 +156,7 @@ private static Object[][] pointExplanationCases() { {"An answer containing an explanation without a matching point receives zero marks", question, "{\"pnt1\": 0, \"pnt2\": 0, \"expl1\": 1, \"expl2\": 0}", INCORRECT, NO_MARKS}, {"An answer containing a point and a mismatched explanation receives one mark", - question, "{\"pnt1\": 1, \"pnt2\": 0, \"expl1\": 0, \"expl2\": 0}", CORRECT, ONE_MARK}}; + question, "{\"pnt1\": 1, \"pnt2\": 0, \"expl1\": 0, \"expl2\": 0}", INCORRECT, ONE_MARK}}; } } diff --git a/src/test/resources/test-postgres-rutherford-data-dump.sql b/src/test/resources/test-postgres-rutherford-data-dump.sql index c28660a359..b5dab6ea91 100644 --- a/src/test/resources/test-postgres-rutherford-data-dump.sql +++ b/src/test/resources/test-postgres-rutherford-data-dump.sql @@ -160,14 +160,14 @@ COPY public.logged_events (id, user_id, anonymous_user, event_type, event_detail -- Data for Name: question_attempts; Type: TABLE DATA; Schema: public; Owner: rutherford -- -COPY public.question_attempts (id, user_id, page_id, question_id, question_attempt, correct, "timestamp") FROM stdin; -2 7 _regression_test_ _regression_test_|acc_multi_q|_regression_test_multi_ {"answer": {"type": "choice", "value": "$42$", "correct": false, "children": [], "encoding": "markdown"}, "correct": true, "questionId": "_regression_test_|acc_multi_q|_regression_test_multi_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice.", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449470730} t 2024-04-18 15:11:10.73 -3 7 _regression_test_ _regression_test_|acc_numeric_q|_regresssion_test_numeric_ {"answer": {"type": "quantity", "units": "m\\\\,s^{-1}", "value": "2.01", "correct": false, "children": []}, "correct": true, "questionId": "_regression_test_|acc_numeric_q|_regresssion_test_numeric_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice.", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "correctUnits": true, "correctValue": true, "dateAttempted": 1713449479419} t 2024-04-18 15:11:19.419 -4 7 _regression_test_ _regression_test_|acc_symbolic_q|_regression_test_symbolic_ {"answer": {"type": "formula", "value": "{\\"result\\":{\\"tex\\":\\"x\\",\\"mhchem\\":\\"\\",\\"python\\":\\"x\\",\\"mathml\\":\\"x\\",\\"uniqueSymbols\\":\\"x\\"},\\"symbols\\":[{\\"type\\":\\"Symbol\\",\\"position\\":{\\"x\\":239.5,\\"y\\":322.3333333333333},\\"expression\\":{\\"latex\\":\\"x\\",\\"python\\":\\"x\\"},\\"properties\\":{\\"letter\\":\\"x\\",\\"modifier\\":\\"\\"}}],\\"textEntry\\":true,\\"userInput\\":\\"x\\"}", "correct": false, "children": [], "pythonExpression": "x", "requiresExactMatch": false}, "correct": true, "questionId": "_regression_test_|acc_symbolic_q|_regression_test_symbolic_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice. It requires an exact match!", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449484110} t 2024-04-18 15:11:24.11 -5 7 _regression_test_ _regression_test_|acc_stringmatch_q|_regression_test_stringmatch_ {"answer": {"type": "stringChoice", "value": "hello", "correct": false, "children": [], "caseInsensitive": false}, "correct": true, "questionId": "_regression_test_|acc_stringmatch_q|_regression_test_stringmatch_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This needs a lower case \\"h\\".", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449505160} t 2024-04-18 15:11:45.16 -6 7 _regression_test_ _regression_test_|acc_chemistry_q|_regression_test_chemistry_ {"answer": {"type": "chemicalFormula", "value": "{\\"result\\":{\\"tex\\":\\"\\\\\\\\text{H} + \\\\\\\\text{Cl}\\",\\"mhchem\\":\\"H + Cl\\",\\"python\\":\\"\\\\\\\\text{H}\\",\\"mathml\\":\\"H+Cl\\",\\"uniqueSymbols\\":\\"H, Cl\\"},\\"symbols\\":[{\\"type\\":\\"ChemicalElement\\",\\"position\\":{\\"x\\":392.575,\\"y\\":531},\\"expression\\":{\\"latex\\":\\"\\\\\\\\text{H} + \\\\\\\\text{Cl}\\",\\"python\\":\\"\\\\\\\\text{H}\\"},\\"children\\":{\\"right\\":{\\"type\\":\\"BinaryOperation\\",\\"children\\":{\\"right\\":{\\"type\\":\\"ChemicalElement\\",\\"properties\\":{\\"element\\":\\"Cl\\"}}},\\"properties\\":{\\"operation\\":\\"+\\"}}},\\"properties\\":{\\"element\\":\\"H\\"}}],\\"textEntry\\":false,\\"userInput\\":\\"\\"}", "correct": false, "children": [], "mhchemExpression": "H + Cl"}, "correct": true, "questionId": "_regression_test_|acc_chemistry_q|_regression_test_chemistry_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice.", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449507230} t 2024-04-18 15:11:47.23 -7 7 _regression_test_ _regression_test_|acc_freetext_q|_regression_test_freetext_ {"answer": {"type": "stringChoice", "value": "it didn't", "correct": false, "children": [], "caseInsensitive": false}, "correct": false, "questionId": "_regression_test_|acc_freetext_q|_regression_test_freetext_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "Spoil sport!", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449514726} f 2024-04-18 15:11:54.726 -8 7 _regression_test_ _regression_test_|_regression_test_logic_ {"answer": {"type": "logicFormula", "value": "{\\"result\\":{\\"tex\\":\\"A \\\\\\\\land B\\",\\"mhchem\\":\\"\\",\\"python\\":\\"A & B\\",\\"mathml\\":\\"\\",\\"uniqueSymbols\\":\\"A, B\\"},\\"symbols\\":[{\\"type\\":\\"Symbol\\",\\"position\\":{\\"x\\":284.625,\\"y\\":482},\\"expression\\":{\\"latex\\":\\"A \\\\\\\\land B\\",\\"python\\":\\"A & B\\"},\\"children\\":{\\"right\\":{\\"type\\":\\"LogicBinaryOperation\\",\\"children\\":{\\"right\\":{\\"type\\":\\"Symbol\\",\\"properties\\":{\\"letter\\":\\"B\\",\\"modifier\\":\\"\\"}}},\\"properties\\":{\\"operation\\":\\"and\\"}}},\\"properties\\":{\\"letter\\":\\"A\\",\\"modifier\\":\\"\\"}}],\\"textEntry\\":false,\\"userInput\\":\\"\\"}", "correct": false, "children": [], "pythonExpression": "A & B", "requiresExactMatch": false}, "correct": true, "questionId": "_regression_test_|_regression_test_logic_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This simplifies to $\\\\and{A}{B}$!", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449529969} t 2024-04-18 15:12:09.969 +COPY public.question_attempts (id, user_id, page_id, question_id, question_attempt, correct, "timestamp", marks) FROM stdin; +2 7 _regression_test_ _regression_test_|acc_multi_q|_regression_test_multi_ {"answer": {"type": "choice", "value": "$42$", "correct": false, "children": [], "encoding": "markdown"}, "correct": true, "questionId": "_regression_test_|acc_multi_q|_regression_test_multi_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice.", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449470730} t 2024-04-18 15:11:10.73 1 +3 7 _regression_test_ _regression_test_|acc_numeric_q|_regresssion_test_numeric_ {"answer": {"type": "quantity", "units": "m\\\\,s^{-1}", "value": "2.01", "correct": false, "children": []}, "correct": true, "questionId": "_regression_test_|acc_numeric_q|_regresssion_test_numeric_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice.", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "correctUnits": true, "correctValue": true, "dateAttempted": 1713449479419} t 2024-04-18 15:11:19.419 1 +4 7 _regression_test_ _regression_test_|acc_symbolic_q|_regression_test_symbolic_ {"answer": {"type": "formula", "value": "{\\"result\\":{\\"tex\\":\\"x\\",\\"mhchem\\":\\"\\",\\"python\\":\\"x\\",\\"mathml\\":\\"x\\",\\"uniqueSymbols\\":\\"x\\"},\\"symbols\\":[{\\"type\\":\\"Symbol\\",\\"position\\":{\\"x\\":239.5,\\"y\\":322.3333333333333},\\"expression\\":{\\"latex\\":\\"x\\",\\"python\\":\\"x\\"},\\"properties\\":{\\"letter\\":\\"x\\",\\"modifier\\":\\"\\"}}],\\"textEntry\\":true,\\"userInput\\":\\"x\\"}", "correct": false, "children": [], "pythonExpression": "x", "requiresExactMatch": false}, "correct": true, "questionId": "_regression_test_|acc_symbolic_q|_regression_test_symbolic_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice. It requires an exact match!", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449484110} t 2024-04-18 15:11:24.11 1 +5 7 _regression_test_ _regression_test_|acc_stringmatch_q|_regression_test_stringmatch_ {"answer": {"type": "stringChoice", "value": "hello", "correct": false, "children": [], "caseInsensitive": false}, "correct": true, "questionId": "_regression_test_|acc_stringmatch_q|_regression_test_stringmatch_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This needs a lower case \\"h\\".", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449505160} t 2024-04-18 15:11:45.16 1 +6 7 _regression_test_ _regression_test_|acc_chemistry_q|_regression_test_chemistry_ {"answer": {"type": "chemicalFormula", "value": "{\\"result\\":{\\"tex\\":\\"\\\\\\\\text{H} + \\\\\\\\text{Cl}\\",\\"mhchem\\":\\"H + Cl\\",\\"python\\":\\"\\\\\\\\text{H}\\",\\"mathml\\":\\"H+Cl\\",\\"uniqueSymbols\\":\\"H, Cl\\"},\\"symbols\\":[{\\"type\\":\\"ChemicalElement\\",\\"position\\":{\\"x\\":392.575,\\"y\\":531},\\"expression\\":{\\"latex\\":\\"\\\\\\\\text{H} + \\\\\\\\text{Cl}\\",\\"python\\":\\"\\\\\\\\text{H}\\"},\\"children\\":{\\"right\\":{\\"type\\":\\"BinaryOperation\\",\\"children\\":{\\"right\\":{\\"type\\":\\"ChemicalElement\\",\\"properties\\":{\\"element\\":\\"Cl\\"}}},\\"properties\\":{\\"operation\\":\\"+\\"}}},\\"properties\\":{\\"element\\":\\"H\\"}}],\\"textEntry\\":false,\\"userInput\\":\\"\\"}", "correct": false, "children": [], "mhchemExpression": "H + Cl"}, "correct": true, "questionId": "_regression_test_|acc_chemistry_q|_regression_test_chemistry_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This is a correct choice.", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449507230} t 2024-04-18 15:11:47.23 1 +7 7 _regression_test_ _regression_test_|acc_freetext_q|_regression_test_freetext_ {"answer": {"type": "stringChoice", "value": "it didn't", "correct": false, "children": [], "caseInsensitive": false}, "correct": false, "questionId": "_regression_test_|acc_freetext_q|_regression_test_freetext_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "Spoil sport!", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449514726} f 2024-04-18 15:11:54.726 0 +8 7 _regression_test_ _regression_test_|_regression_test_logic_ {"answer": {"type": "logicFormula", "value": "{\\"result\\":{\\"tex\\":\\"A \\\\\\\\land B\\",\\"mhchem\\":\\"\\",\\"python\\":\\"A & B\\",\\"mathml\\":\\"\\",\\"uniqueSymbols\\":\\"A, B\\"},\\"symbols\\":[{\\"type\\":\\"Symbol\\",\\"position\\":{\\"x\\":284.625,\\"y\\":482},\\"expression\\":{\\"latex\\":\\"A \\\\\\\\land B\\",\\"python\\":\\"A & B\\"},\\"children\\":{\\"right\\":{\\"type\\":\\"LogicBinaryOperation\\",\\"children\\":{\\"right\\":{\\"type\\":\\"Symbol\\",\\"properties\\":{\\"letter\\":\\"B\\",\\"modifier\\":\\"\\"}}},\\"properties\\":{\\"operation\\":\\"and\\"}}},\\"properties\\":{\\"letter\\":\\"A\\",\\"modifier\\":\\"\\"}}],\\"textEntry\\":false,\\"userInput\\":\\"\\"}", "correct": false, "children": [], "pythonExpression": "A & B", "requiresExactMatch": false}, "correct": true, "questionId": "_regression_test_|_regression_test_logic_", "explanation": {"tags": [], "type": "content", "children": [{"tags": [], "type": "content", "value": "This simplifies to $\\\\and{A}{B}$!", "children": [], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}], "encoding": "markdown", "canonicalSourceFile": "content/_regression_test_.json"}, "dateAttempted": 1713449529969} t 2024-04-18 15:12:09.969 1 \. @@ -191,7 +191,7 @@ COPY public.quiz_attempts (id, user_id, quiz_id, quiz_assignment_id, start_date, -- Data for Name: quiz_question_attempts; Type: TABLE DATA; Schema: public; Owner: rutherford -- -COPY public.quiz_question_attempts (id, quiz_attempt_id, question_id, question_attempt, correct, "timestamp") FROM stdin; +COPY public.quiz_question_attempts (id, quiz_attempt_id, question_id, question_attempt, correct, "timestamp", marks) FROM stdin; \.