From b132b19553f0032ef80b7997132bbb02ce2cb77c Mon Sep 17 00:00:00 2001 From: Ehan A Date: Sat, 18 Jul 2026 17:52:32 -0400 Subject: [PATCH 1/5] Allow unanswered questions in GraphQL schema https://codeberg.org/quizfreely/quizfreely/issues/30 --- graph/mutation.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graph/mutation.graphqls b/graph/mutation.graphqls index d3309df..c51a49b 100644 --- a/graph/mutation.graphqls +++ b/graph/mutation.graphqls @@ -40,14 +40,14 @@ input MCQInput { answerWith: AnswerWith! correct: Boolean! correctChoiceIndex: Int! - answeredIndex: Int! + answeredIndex: Int distractors: [TermATPInput!]! } input TFQInput { term: TermATPInput! answerWith: AnswerWith! correct: Boolean! - answeredBool: Boolean! + answeredBool: Boolean distractor: TermATPInput } input FRQInput { @@ -55,7 +55,7 @@ input FRQInput { answerWith: AnswerWith! correct: Boolean! userMarkedCorrect: Boolean - answeredString: String! + answeredString: String } input MatchActivityInput { durationMs: Int! From 92311bd913e6b8d597c69cdb217e1e59af5f4788 Mon Sep 17 00:00:00 2001 From: Ehan A Date: Sat, 18 Jul 2026 17:53:05 -0400 Subject: [PATCH 2/5] Allow unanswered result in gql schema https://codeberg.org/quizfreely/quizfreely/issues/30 --- graph/query.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/graph/query.graphqls b/graph/query.graphqls index bf652dd..616bd07 100644 --- a/graph/query.graphqls +++ b/graph/query.graphqls @@ -59,14 +59,14 @@ type MCQ { answerWith: AnswerWith! correct: Boolean! correctChoiceIndex: Int! - answeredIndex: Int! + answeredIndex: Int distractors: [TermATP!]! } type TFQ { term: TermATP! answerWith: AnswerWith! correct: Boolean! - answeredBool: Boolean! + answeredBool: Boolean distractor: TermATP } type FRQ { @@ -74,7 +74,7 @@ type FRQ { answerWith: AnswerWith! correct: Boolean! userMarkedCorrect: Boolean - answeredString: String! + answeredString: String } type MatchActivity { id: ID! From 25b118b27ae9c906b75a1c1cd272dc7147315c9a Mon Sep 17 00:00:00 2001 From: Ehan A Date: Sat, 18 Jul 2026 17:53:23 -0400 Subject: [PATCH 3/5] use correct nullability for unanswered questions https://codeberg.org/quizfreely/quizfreely/issues/30 --- graph/resolver/mutation.resolvers.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/graph/resolver/mutation.resolvers.go b/graph/resolver/mutation.resolvers.go index ac24c59..6c5ad34 100644 --- a/graph/resolver/mutation.resolvers.go +++ b/graph/resolver/mutation.resolvers.go @@ -741,22 +741,22 @@ RETURNING choices[j] = mcq.Distractors[j-1] } } - if mcq.AnsweredIndex >= 0 && mcq.AnsweredIndex < int32(len(choices)) { - answeredTermID = &choices[mcq.AnsweredIndex].ID + if mcq.AnsweredIndex != nil && *mcq.AnsweredIndex >= 0 && int(*mcq.AnsweredIndex) < len(choices) { + answeredTermID = &choices[*mcq.AnsweredIndex].ID } } else if qInput.Tfq != nil { tfq := qInput.Tfq if tfq.Correct { answeredTermID = &tfq.Term.ID } else { - if tfq.AnsweredBool { + if tfq.AnsweredBool != nil && *tfq.AnsweredBool { if tfq.Distractor != nil { answeredTermID = &tfq.Distractor.ID } } } } else if qInput.Frq != nil { - answeredString = &qInput.Frq.AnsweredString + answeredString = qInput.Frq.AnsweredString } reviewEventArgs = append(reviewEventArgs, @@ -1030,7 +1030,9 @@ func (r *mutationResolver) UpdatePracticeTestQuestion(ctx context.Context, id st AnswerWith: row.AnswerWith, Correct: newCorrect, UserMarkedCorrect: &umc, - AnsweredString: data["answeredString"].(string), + } + if answeredStr, ok := data["answeredString"].(string); ok { + q.Frq.AnsweredString = &answeredStr } return q, nil From 86aa91997d5f9f829158dbd75098e69e508106a9 Mon Sep 17 00:00:00 2001 From: Ehan A Date: Sat, 18 Jul 2026 17:53:41 -0400 Subject: [PATCH 4/5] use correct nullability for query https://codeberg.org/quizfreely/quizfreely/issues/30 --- graph/resolver/query.resolvers.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/graph/resolver/query.resolvers.go b/graph/resolver/query.resolvers.go index bd08c7f..29de1e6 100644 --- a/graph/resolver/query.resolvers.go +++ b/graph/resolver/query.resolvers.go @@ -103,7 +103,7 @@ func (r *practiceTestResolver) Questions(ctx context.Context, obj *model.Practic var data struct { Distractors []*model.TermAtp `json:"distractors"` CorrectChoiceIndex int32 `json:"correctChoiceIndex"` - AnsweredIndex int32 `json:"answeredIndex"` + AnsweredIndex *int32 `json:"answeredIndex"` } if err := json.Unmarshal(row.Data, &data); err != nil { return nil, fmt.Errorf("failed to unmarshal MCQ data: %w", err) @@ -119,7 +119,7 @@ func (r *practiceTestResolver) Questions(ctx context.Context, obj *model.Practic } case "TFQ": var data struct { - AnsweredBool bool `json:"answeredBool"` + AnsweredBool *bool `json:"answeredBool"` Distractor *model.TermAtp `json:"distractor"` } if err := json.Unmarshal(row.Data, &data); err != nil { @@ -135,8 +135,8 @@ func (r *practiceTestResolver) Questions(ctx context.Context, obj *model.Practic } case "FRQ": var data struct { - AnsweredString string `json:"answeredString"` - UserMarkedCorrect bool `json:"userMarkedCorrect"` + AnsweredString *string `json:"answeredString"` + UserMarkedCorrect bool `json:"userMarkedCorrect"` } if err := json.Unmarshal(row.Data, &data); err != nil { return nil, fmt.Errorf("failed to unmarshal FRQ data: %w", err) From 53b2e0d937078d1f0ce95c5a620a36f0fea3efeb Mon Sep 17 00:00:00 2001 From: Ehan A Date: Sat, 18 Jul 2026 17:53:57 -0400 Subject: [PATCH 5/5] regenerate after fixing unanswered question logic https://codeberg.org/quizfreely/quizfreely/issues/30 --- graph/generated.go | 36 +++++++++--------------------------- graph/model/models_gen.go | 12 ++++++------ 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/graph/generated.go b/graph/generated.go index e42ca34..2cb737c 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -3318,14 +3318,11 @@ func (ec *executionContext) _FRQ_answeredString(ctx context.Context, field graph return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*string) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_FRQ_answeredString(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -4894,14 +4891,11 @@ func (ec *executionContext) _MCQ_answeredIndex(ctx context.Context, field graphq return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(int32) + res := resTmp.(*int32) fc.Result = res - return ec.marshalNInt2int32(ctx, field.Selections, res) + return ec.marshalOInt2ᚖint32(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_MCQ_answeredIndex(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -10279,14 +10273,11 @@ func (ec *executionContext) _TFQ_answeredBool(ctx context.Context, field graphql return graphql.Null } if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } return graphql.Null } - res := resTmp.(bool) + res := resTmp.(*bool) fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_TFQ_answeredBool(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -13765,7 +13756,7 @@ func (ec *executionContext) unmarshalInputFRQInput(ctx context.Context, obj any) it.UserMarkedCorrect = data case "answeredString": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("answeredString")) - data, err := ec.unmarshalNString2string(ctx, v) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err } @@ -13986,7 +13977,7 @@ func (ec *executionContext) unmarshalInputMCQInput(ctx context.Context, obj any) it.CorrectChoiceIndex = data case "answeredIndex": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("answeredIndex")) - data, err := ec.unmarshalNInt2int32(ctx, v) + data, err := ec.unmarshalOInt2ᚖint32(ctx, v) if err != nil { return it, err } @@ -14239,7 +14230,7 @@ func (ec *executionContext) unmarshalInputTFQInput(ctx context.Context, obj any) it.Correct = data case "answeredBool": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("answeredBool")) - data, err := ec.unmarshalNBoolean2bool(ctx, v) + data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) if err != nil { return it, err } @@ -14511,9 +14502,6 @@ func (ec *executionContext) _FRQ(ctx context.Context, sel ast.SelectionSet, obj out.Values[i] = ec._FRQ_userMarkedCorrect(ctx, field, obj) case "answeredString": out.Values[i] = ec._FRQ_answeredString(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -15003,9 +14991,6 @@ func (ec *executionContext) _MCQ(ctx context.Context, sel ast.SelectionSet, obj } case "answeredIndex": out.Values[i] = ec._MCQ_answeredIndex(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } case "distractors": out.Values[i] = ec._MCQ_distractors(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -16697,9 +16682,6 @@ func (ec *executionContext) _TFQ(ctx context.Context, sel ast.SelectionSet, obj } case "answeredBool": out.Values[i] = ec._TFQ_answeredBool(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } case "distractor": out.Values[i] = ec._TFQ_distractor(ctx, field, obj) default: diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index 59b4c08..8251654 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -14,7 +14,7 @@ type Frq struct { AnswerWith AnswerWith `json:"answerWith"` Correct bool `json:"correct"` UserMarkedCorrect *bool `json:"userMarkedCorrect,omitempty"` - AnsweredString string `json:"answeredString"` + AnsweredString *string `json:"answeredString,omitempty"` } type FRQInput struct { @@ -22,7 +22,7 @@ type FRQInput struct { AnswerWith AnswerWith `json:"answerWith"` Correct bool `json:"correct"` UserMarkedCorrect *bool `json:"userMarkedCorrect,omitempty"` - AnsweredString string `json:"answeredString"` + AnsweredString *string `json:"answeredString,omitempty"` } type FSRSCard struct { @@ -88,7 +88,7 @@ type Mcq struct { AnswerWith AnswerWith `json:"answerWith"` Correct bool `json:"correct"` CorrectChoiceIndex int32 `json:"correctChoiceIndex"` - AnsweredIndex int32 `json:"answeredIndex"` + AnsweredIndex *int32 `json:"answeredIndex,omitempty"` Distractors []*TermAtp `json:"distractors"` } @@ -97,7 +97,7 @@ type MCQInput struct { AnswerWith AnswerWith `json:"answerWith"` Correct bool `json:"correct"` CorrectChoiceIndex int32 `json:"correctChoiceIndex"` - AnsweredIndex int32 `json:"answeredIndex"` + AnsweredIndex *int32 `json:"answeredIndex,omitempty"` Distractors []*TermATPInput `json:"distractors"` } @@ -164,7 +164,7 @@ type Tfq struct { Term *TermAtp `json:"term"` AnswerWith AnswerWith `json:"answerWith"` Correct bool `json:"correct"` - AnsweredBool bool `json:"answeredBool"` + AnsweredBool *bool `json:"answeredBool,omitempty"` Distractor *TermAtp `json:"distractor,omitempty"` } @@ -172,7 +172,7 @@ type TFQInput struct { Term *TermATPInput `json:"term"` AnswerWith AnswerWith `json:"answerWith"` Correct bool `json:"correct"` - AnsweredBool bool `json:"answeredBool"` + AnsweredBool *bool `json:"answeredBool,omitempty"` Distractor *TermATPInput `json:"distractor,omitempty"` }