-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kevin/vxmarkscan warn when interpretation fails #3981
Conversation
9ea3d8a
to
683054b
Compare
}, | ||
on: { | ||
PAPER_PARKED: 'done', | ||
NO_PAPER_ANYWHERE: 'accepting_paper', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle case of user pulling paper out of the infeed before it's fully loaded. Jammed paper is handled by top-level on: { PAPER_JAM: 'jammed' }
transition
46c904d
to
56a0845
Compare
}; | ||
} | ||
|
||
function pollAuthStatus(): InvokeConfig<Context, PaperHandlerStatusEvent> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love adding more polling, but this seemed cleaner than expecting the frontend to "watch" auth changes and change the backend state machine state via useEffect
and mutation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that polling on the backend is cleaner
pollWorkerAuth.cardlessVoterUser && | ||
// It's expected there are votes in app state if the state machine reports a blank page after printing. | ||
// The paper was likely inserted upside down so the solution is to reload paper correctly and go back to | ||
// the voting screen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like leaving super specific comments like this that might diverge from implementation, but I think it's important to explain why we're ok with breaking a previous constraint (that you can't enter a voting session with existing frontend ballot state)
timeout({ | ||
each: PAPER_HANDLER_STATUS_POLLING_TIMEOUT_MS, | ||
with: () => throwError(() => new Error('paper_status_timed_out')), | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these be updated for auth status?
}; | ||
} | ||
|
||
function pollAuthStatus(): InvokeConfig<Context, PaperHandlerStatusEvent> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that polling on the backend is cleaner
cond: (context) => | ||
context.interpretation | ||
? context.interpretation[0].interpretation.type === 'BlankPage' | ||
: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if there is no interpretation? It seems like both conds would be false and we'd get stuck in this state. Seems like maybe we expect context.interpretation
to always be defined, in which case I think an assert
would be more clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking with the assert that you could remove the conditional check. So something like this:
assert(context.interpretation)
return context.interpretation[0].interpretation.type === 'BlankPage`
or even
assertDefined(context.interpretation)[0].interpretation.type === 'BlankPage'
Sorry I wasn't clear enough!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops sorry your original comment was clear, just rushed the fix on my part. 700b335 uses assertDefined
and removes the conditional check.
It also adds an assertion on interpretation type in entry
. It's redundant with the check in the cond
transitions but
- it's valuable to throw on unexpected interpretation types. Without this assertion we'd get stuck in
transition_interpretation
- Typescript isn't aware that
entry
is guaranteed to precedecond
transitions so we need to re-assert withassertDefined
blank_page_interpretation: { | ||
invoke: pollPaperStatus(), | ||
entry: async (context) => { | ||
await context.driver.presentPaper(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to call presentPaper
here if we also call it in presenting_paper
?
NO_PAPER_ANYWHERE: 'accepting_paper', | ||
}, | ||
}, | ||
done: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could probably remove this state by adding the assign
action contained here to the transition from load_paper
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree the assign
call can go anywhere, but the state machine breaks when I get rid of the done
state and transitioning from load_paper
: https://app.circleci.com/pipelines/github/votingworks/vxsuite/12078/workflows/b8f38ca0-e4cd-4e24-bc63-48b103b367a0/jobs/400580
It's expecting the target state to be a sibling of load_paper
ie. a child of blank_page_interpretation
unless we transition from the done
state. When transitioning from done
it can successfully transition to top-level states.
entry: () => { | ||
debug('Placeholder paper_reloaded'); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove debug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I think I cleaned this up but accidentally overwrote with a git reset
before committing.
case state.matches('transition_interpretation'): | ||
return 'transition_interpretation'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a state the frontend needs to know about? Seems internal to the state machine. I generally have taken the approach of only exposing states to the frontend that should show something different to the user
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The frontend doesn't need to know about it, but if we don't handle it then the switch statement will fall through to no_hardware
and the frontend will show an error. The chances of that happening are pretty slim - it would have to query status at the exact moment the transition state is running.
I think the best way to handle is to just return interpreting
here.
const history = useHistory(); | ||
useEffect(() => { | ||
history.push('/ready-to-review'); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things seem to be getting quite complicated with the rendering of screens and routing on the frontend. Even with the comments, I'm having trouble figuring out what's going on.
The approach that VxScan used of having the frontend manage auth and the backend manage the scanning flow was much simpler because the entire scanning flow lives within the voter view - the poll worker and election manager sections of the app don't interact with the scanner.
I don't think this paradigm works well for BMDs (especially not for this more complex print and scan device), since the poll worker and voter are both deeply involved in the voting flow.
Now that we've got auth setup on the backend, I think we might want to consider managing all of the voting flow on the backend. That would look like having one VotingFlow
component on the frontend with a big switch
for all of the machine states, and not using frontend routing within that component.
What do you think? I'm not down in the details so this may not be as easy as it sounds from my vantage point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree - if we can get rid of frontend routing it would be a big win. Not sure yet if doing that is blocked by migrating the remaining legacy frontend state to the backend. I think this change would be too big to fit into this PR so I've created #3985 to track.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I agree it's probably better to do as a separate refactor.
cond: (context) => | ||
context.interpretation | ||
? context.interpretation[0].interpretation.type === 'BlankPage' | ||
: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking with the assert that you could remove the conditional check. So something like this:
assert(context.interpretation)
return context.interpretation[0].interpretation.type === 'BlankPage`
or even
assertDefined(context.interpretation)[0].interpretation.type === 'BlankPage'
Sorry I wasn't clear enough!
QA'd these changes after 700b335 |
Overview
Adds a flow for when the ballot interpretation is
BlankPageInterpretation
. Barring hardware failures, we expect this to happen only when the thermal paper is inserted upside down.Demo Video or Screenshot
In this video I load non-thermal paper first, then load thermal paper in the correct orientation second.
IMG_4256.MOV
Known issues in this video:
Testing Plan
Checklist