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..f82b829bcc 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, marks, \"timestamp\")" + + " VALUES (?, ?, ?::text::jsonb, ?, ?, ?);"; try (Connection conn = database.getDatabaseConnection(); PreparedStatement pst = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); ) { @@ -73,7 +73,14 @@ public void registerQuestionAttempt(Long quizAttemptId, QuestionValidationRespon } else { pst.setNull(4, Types.BOOLEAN); } - pst.setTimestamp(5, new java.sql.Timestamp(questionResponse.getDateAttempted().getTime())); + + if (questionResponse.getMarks() != null) { + pst.setInt(5, questionResponse.getMarks()); + } else { + pst.setInt(5, java.sql.Types.NULL); + } + + pst.setTimestamp(6, new java.sql.Timestamp(questionResponse.getDateAttempted().getTime())); 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..4679bb57ce 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 @@ -1,5 +1,6 @@ package uk.ac.cam.cl.dtg.isaac.dos; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import uk.ac.cam.cl.dtg.isaac.dos.content.Choice; import uk.ac.cam.cl.dtg.isaac.dos.content.Content; import uk.ac.cam.cl.dtg.isaac.dos.content.DTOMapping; @@ -9,9 +10,9 @@ import java.util.Date; import java.util.List; +@JsonIgnoreProperties({ "marksAwarded" }) @DTOMapping(LLMFreeTextQuestionValidationResponseDTO.class) public class LLMFreeTextQuestionValidationResponse extends QuestionValidationResponse { - private Integer marksAwarded; private List markBreakdown; public LLMFreeTextQuestionValidationResponse() { @@ -23,13 +24,6 @@ public LLMFreeTextQuestionValidationResponse(final String questionId, final Choi super(questionId, answer, correct, explanation, dateAttempted); } - public Integer getMarksAwarded() { - return marksAwarded; - } - public void setMarksAwarded(Integer marksAwarded) { - this.marksAwarded = marksAwarded; - } - public List getMarkBreakdown() { return markBreakdown; } 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..4c108b9afe 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. @@ -25,6 +26,26 @@ public LightweightQuestionValidationResponse() { * - * @param correct * - + * @param marks + * - + * @param dateAttempted + * - + */ + public LightweightQuestionValidationResponse(final String questionId, final Boolean correct, + final Integer marks, final Date dateAttempted) { + this.questionId = questionId; + this.correct = correct; + this.marks = marks; + this.dateAttempted = dateAttempted; + } + + /** + * Constructor without specifying marks (instead derived from 'correct') + * + * @param questionId + * - + * @param correct + * - * @param dateAttempted * - */ @@ -32,6 +53,7 @@ public LightweightQuestionValidationResponse(final String questionId, final Bool final Date dateAttempted) { this.questionId = questionId; this.correct = correct; + this.marks = (correct != null && correct) ? 1 : 0; this.dateAttempted = dateAttempted; } @@ -73,6 +95,25 @@ public final void setCorrect(final Boolean correct) { this.correct = correct; } + /** + * 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; + } + /** * Gets the dateAttempted. * @@ -95,6 +136,6 @@ public void setDateAttempted(final Date dateAttempted) { @Override public String toString() { return "QuestionValidationResponse [questionId=" + questionId + ", correct=" + correct + - ", dateAttempted=" + dateAttempted + "]"; + ", marks=" + marks + ", dateAttempted=" + dateAttempted + "]"; } } 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..1487e9f29b 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 @@ -47,6 +47,29 @@ public QuestionValidationResponse() { * - * @param correct * - + * @param marks + * - + * @param explanation + * - + * @param dateAttempted + * - + */ + public QuestionValidationResponse(final String questionId, final Choice answer, final Boolean correct, + final Integer marks, final Content explanation, final Date dateAttempted) { + super(questionId, correct, marks, dateAttempted); + this.answer = answer; + this.explanation = explanation; + } + + /** + * Constructor without specifying marks (instead derived from 'correct') + * + * @param questionId + * - + * @param answer + * - + * @param correct + * - * @param explanation * - * @param dateAttempted @@ -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() + "]"; + ", marks=" + super.getMarks() + ", dateAttempted=" + super.getDateAttempted() + "]"; } } 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..20b85bef0a 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 @@ -1,23 +1,17 @@ package uk.ac.cam.cl.dtg.isaac.dto; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import uk.ac.cam.cl.dtg.isaac.dto.content.LLMFreeTextMarkSchemeEntryDTO; import java.util.List; +@JsonIgnoreProperties({ "marksAwarded" }) public class LLMFreeTextQuestionValidationResponseDTO extends QuestionValidationResponseDTO { - private Integer marksAwarded; private List markBreakdown; public LLMFreeTextQuestionValidationResponseDTO() { } - public Integer getMarksAwarded() { - return marksAwarded; - } - public void setMarksAwarded(Integer marksAwarded) { - this.marksAwarded = marksAwarded; - } - public List getMarkBreakdown() { return markBreakdown; } 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..75d368b55c 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 @@ -17,6 +17,8 @@ import java.util.Date; +import uk.ac.cam.cl.dtg.isaac.dos.content.Choice; +import uk.ac.cam.cl.dtg.isaac.dos.content.Content; import uk.ac.cam.cl.dtg.isaac.dto.content.ChoiceDTO; import uk.ac.cam.cl.dtg.isaac.dto.content.ContentDTO; @@ -28,6 +30,7 @@ public class QuestionValidationResponseDTO { private String questionId; private ChoiceDTO answer; private Boolean correct; + private Integer marks; private ContentDTO explanation; private Date dateAttempted; @@ -47,20 +50,48 @@ public QuestionValidationResponseDTO() { * - * @param correct * - + * @param marks + * - + * @param explanation + * - + * @param dateAttempted + * - + */ + public QuestionValidationResponseDTO(final String questionId, final ChoiceDTO answer, final Boolean correct, + final Integer marks, final ContentDTO explanation, final Date dateAttempted) { + this.questionId = questionId; + this.answer = answer; + this.correct = correct; + this.marks = marks; + this.explanation = explanation; + this.dateAttempted = dateAttempted; + } + + /** + * Constructor without specifying marks (instead derived from 'correct') + * + * @param questionId + * - + * @param answer + * - + * @param correct + * - * @param explanation * - * @param dateAttempted * - */ public QuestionValidationResponseDTO(final String questionId, final ChoiceDTO answer, final Boolean correct, - final ContentDTO explanation, final Date dateAttempted) { + final ContentDTO explanation, final Date dateAttempted) { this.questionId = questionId; this.answer = answer; this.correct = correct; + this.marks = (correct != null && correct) ? 1 : 0; this.explanation = explanation; this.dateAttempted = dateAttempted; } + /** * Gets the questionId. * @@ -118,6 +149,25 @@ public final void setCorrect(final Boolean correct) { this.correct = correct; } + /** + * 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; + } + /** * Gets the explanation. * @@ -159,6 +209,6 @@ public void setDateAttempted(final Date dateAttempted) { @Override public String toString() { return "QuestionValidationResponseDTO [questionId=" + questionId + ", answer=" + answer + ", correct=" - + correct + ", explanation=" + explanation + ", dateAttempted=" + dateAttempted + "]"; + + correct + ", marks=" + marks + ", explanation=" + explanation + ", dateAttempted=" + dateAttempted + "]"; } } 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..0ac5c11477 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,6 +276,7 @@ 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( @@ -294,7 +295,7 @@ private LLMFreeTextQuestionValidationResponse generateQuestionValidationResponse LLMFreeTextQuestionValidationResponse validationResponse = new LLMFreeTextQuestionValidationResponse( question.getId(), answer, isConsideredCorrect, null, new Date()); - validationResponse.setMarksAwarded(markTotal); + validationResponse.setMarks(markTotal); validationResponse.setMarkBreakdown(markBreakdown); return validationResponse; } 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..289dea85b8 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, marks, \"timestamp\")" + + " VALUES (?, ?, ?, ?::text::jsonb, ?, ?, ?);"; try (Connection conn = database.getDatabaseConnection(); PreparedStatement pst = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS); ) { @@ -201,7 +201,14 @@ 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(6, questionAttempt.getMarks()); + } else { + pst.setInt(6, java.sql.Types.NULL); + } + + pst.setTimestamp(7, new java.sql.Timestamp(questionAttempt.getDateAttempted().getTime())); 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, marks, timestamp FROM question_attempts" + " WHERE user_id = ANY(?) AND page_id = ANY(?)" + " ORDER BY \"timestamp\" ASC"; @@ -441,6 +448,12 @@ private LightweightQuestionValidationResponse resultsToLightweightValidationResp partialQuestionAttempt.setCorrect(results.getBoolean("correct")); partialQuestionAttempt.setQuestionId(results.getString("question_id")); partialQuestionAttempt.setDateAttempted(results.getTimestamp("timestamp")); + int marks = results.getInt("marks"); + if (results.wasNull()) { + partialQuestionAttempt.setMarks(0); + } else { + partialQuestionAttempt.setMarks(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..d8d84bf3ee 100644 --- a/src/main/resources/db_scripts/create_anonymous_database.sql +++ b/src/main/resources/db_scripts/create_anonymous_database.sql @@ -151,6 +151,7 @@ CREATE TABLE anonymous.question_attempts AS question_id, question_attempt, correct, + marks, timestamp FROM public.question_attempts; @@ -200,6 +201,7 @@ CREATE TABLE anonymous.quiz_question_attempts AS question_id, question_attempt, correct, + marks, timestamp FROM public.quiz_question_attempts; diff --git a/src/main/resources/db_scripts/migrations/2025-11-questions-attempts-add-mark-field.sql b/src/main/resources/db_scripts/migrations/2025-11-questions-attempts-add-mark-field.sql new file mode 100644 index 0000000000..776bcf136c --- /dev/null +++ b/src/main/resources/db_scripts/migrations/2025-11-questions-attempts-add-mark-field.sql @@ -0,0 +1,5 @@ +ALTER TABLE question_attempts +ADD marks integer; + +ALTER TABLE quiz_question_attempts +ADD marks integer; 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..046854ed5d 100644 --- a/src/main/resources/db_scripts/postgres-rutherford-create-script.sql +++ b/src/main/resources/db_scripts/postgres-rutherford-create-script.sql @@ -346,6 +346,7 @@ CREATE TABLE public.question_attempts ( question_id text NOT NULL, question_attempt jsonb, correct boolean, + marks integer, "timestamp" timestamp without time zone ); @@ -460,6 +461,7 @@ CREATE TABLE public.quiz_question_attempts ( question_id text NOT NULL, question_attempt jsonb, correct boolean, + marks integer, "timestamp" timestamp without time zone ); 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..028da55007 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 @@ -257,7 +257,7 @@ public static void expectMark(LLMFreeTextQuestionValidationResponse response, int marksAwarded, List expectedMarks) { assertEquals(isCorrect, response.isCorrect()); - assertEquals(marksAwarded, (long) response.getMarksAwarded()); + assertEquals(marksAwarded, (long) response.getMarks()); assertTrue(expectedMarks.containsAll(response.getMarkBreakdown())); assertTrue(response.getMarkBreakdown().containsAll(expectedMarks)); } diff --git a/src/test/resources/test-postgres-rutherford-data-dump.sql b/src/test/resources/test-postgres-rutherford-data-dump.sql index c28660a359..71b579c217 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, marks, "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 1 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 1 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 1 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 1 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 1 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 0 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 1 2024-04-18 15:12:09.969 \. @@ -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, marks, "timestamp") FROM stdin; \.