Differentiate 'Not sure' from 'Skip': record a human abstention (issue #712) - #731
Merged
Merged
Conversation
…#712) 'Not sure' means a voter engaged and found the image genuinely ambiguous - information about the card. 'Skip' carries no signal at all. Both used to be no-ops; now 'Not sure' POSTs 2/submitQuestionAbstention/ and is recorded in a new CardQuestionAbstention row (card, anonymous_id, question_type, unique together, get_or_create-idempotent) before the existing Level 1 -> Level 2 transition. 'Skip' is unchanged. CardQuestionAbstention is the human counterpart to CardScanLog's machine abstention and is deliberately a separate model, not a subclass of AbstractWeightedVote - an abstention is not a vote.
APISubmitQuestionAbstention's return type annotation referenced SubmitQuestionAbstentionResponse without importing it from schema_types - the type is exported there, just never pulled into api.ts's import list. Jest's transpile-only transform doesn't type-check, so this only surfaced as a 'next build' compile failure (the build step that only runs on shard 1 of the sharded Playwright job, alongside Jest), not a test failure. Also adds a mocks/handlers.ts handler for the new /2/submitQuestionAbstention/ endpoint (previously unhandled in Playwright's MSW fixture - requests to it silently fell through to the real network) and wires it into the Level 1 NOT SURE Playwright spec, extending that test's existing 'no printing vote cast' assertion with a payload check on the new abstention POST.
…notsure-skip # Conflicts: # .github/coverage-acks.txt
Merged
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
"Not sure" and "Skip" on the What's That Card Level 1 confirm screen were
functionally identical: neither wrote anything to the backend. This
differentiates them per issue #712's design direction: "Not sure" means
the voter engaged and found the image genuinely ambiguous - information
about the card - while "Skip" carries no signal about the card at all.
Investigation before building (issue #712 point 1): checked whether an
existing mechanism already fit.
QuestionFeedServedLoghas nocardcolumn (onlyanonymous_id/pool/question_type/origin_reason/served_at), so it cannotexpress "this voter found this card ambiguous."
CardScanLogrecords abstention, but only for MACHINE calculator runs(
run_id,skip_reason,evidence_types_used,survivor_pksare allcalculator-run bookkeeping) and backs a resume-exclusion query
(
local_identify_printing_tags._eligible_base_queryset) that mustnever be satisfied by a human tap - conflating the two would let one
silently stand in for the other.
CardPrintingTag/CardIllustrationVote/CardTagVote(allAbstractWeightedVotesubclasses) only express positive/no-matchoutcomes, never a no-opinion abstention.
None fit, so this adds one new model:
CardQuestionAbstention(card,anonymous_id,question_type, unique together, deliberately NOT anAbstractWeightedVotesubclass - an abstention is not a vote and mustnever reach
vote_consensus). Tapping "Not sure" now POSTs2/submitQuestionAbstention/(idempotentget_or_create) before thesame
setStage("level2")transition it always did; the voter identity isthe same
getOrCreateAnonymousId()value the feed fetch and every votesubmit already send. "Skip" is unchanged - it still writes nothing
anywhere, and whether it should record a lightweight "served, no answer"
fact is left as an open owner decision, not settled here.
For a future exclusion query (issue #713, not built here): "has this
anonymous_idalready abstained on thiscardfor thisquestion_type"is a single indexed equality lookup against this table's own unique
constraint -
CardQuestionAbstention.objects.filter(card_id=..., anonymous_id=..., question_type=...).exists().Documentation for the new model ships in this PR, extending
docs/features/printing-tags.md's "Unified question feed" section(the neighbouring
QuestionFeedServedLog/CardScanLogmechanisms arealready documented there).
Test plan
MPCAutofill/cardpicker/tests/test_question_abstention.py(new):the submit endpoint records exactly one abstention with the right
voter/card/question-type; repeat taps from the same voter are
idempotent (still one row); different question types or different
voters on the same card record separately; an unknown card
identifier is a 400; a card with no abstention calls has zero rows;
the abstention is queryable by (card, anonymous_id, question_type).
frontend/src/features/questionFeed/QuestionFeed.test.tsx(newcases): tapping Level 1 "Not sure" POSTs the abstention with the
right identifier/questionType and still advances to Level 2;
tapping Level 1 "Skip" never calls
submitQuestionAbstention.test_question_abstention.py,test_question_feed.py,test_printing_tags_views.py,test_moderation_views.py) - 127 passed.pytest cardpicker/ --collect-only) -3692 tests collected, no import/collection errors from the
model/admin/urls/views/schema_types changes.
frontend/src/features/questionFeed/QuestionFeed.test.tsxfullfile - 21 passed, 0 regressions.
pytest cardpicker/) beyond the affected-arearun above was not completed to green/red on every one of the
3692 tests in this session.