Suggest the next problem in a series after a pass
After a pass, the lobby recommended a problem unrelated to the one just passed, and the bank had no way to say that one problem uses the same technique as another one level up. This adds that relation for the 18 pairs listed in the issue, from Easy to Medium and from Medium to Hard, and offers the sequel in the lobby after a pass.
A sequel field in problem-bank/problems.json names the problem one level up. validated_problems refuses a sequel that is missing from the bank or not exactly one level harder, so a pair is changed by editing one line. gen-problem-cards.py writes it onto the card as data-sequel with the sequel's page name, because a problem id on the initial page names the LeetCode problem the card hides; candidate_problem builds its fields up, so the problem pages and the Rust tables are unchanged.
sequelSuggestion in web/problem-picker.js reads the history the lobby already loads: when the latest interview by time passed a problem whose sequel the candidate has not passed, it returns the pair, and otherwise nothing. The lobby renders it as a line under the selected problem with a "Practice it" button, inside .random-picker so Strict hides it with the other pickers. Pressing it selects the sequel across the level filters the way "Pick from my list" selects its draw. pickProblem is untouched, so the selected problem, "Pick a random problem", due reviews and the list draw are what they were until the button is pressed.
#526 also adds an attribute to the card line in gen-problem-cards.py and a field beside topics in problems.json, so whichever of the two lands second rebases onto the other; the two attributes and fields are independent.
For example:
passed Chargeback Pair Match, and suggest Gift Card Bundle Pair (Medium) in the same series.
pressed "practice it", selected problem become Gift Card Bundle Pair (Medium)
Validation: tests/browser/problem-picker.test.js covers a pass from Easy and from Medium, no offer after a fail, an unscored interview, a problem without a sequel or a sequel already passed, the latest interview winning whatever order the reports arrive in, and a sequel with no card. Two lobby tests drive the page: the offer appears beside an unchanged selection and pressing it selects Gift Card Bundle Pair, and a fail shows no offer. tests/test_gen_problems.py covers the one-level rule and a card naming its sequel by page. With the implementation reverted and the tests kept, the Python tests, the picker suite and the offer's lobby test fail. ./scripts/test.sh passes every gate except review-memory-tests, which fails the same way on main here because my gh 2.46 lacks the attestation flags. Not checked locally: cargo-audit, cargo-deny, actionlint and the desktop lane, none of which covers a file this change touches.
Closes #536
Summary by cubic
After a pass, the lobby recommended a problem unrelated to the one just passed. Problems can now name a sequel one level up, and the lobby offers it after a pass.
- Adds a
sequelfield to 18 problems inproblem-bank/problems.json;validated_problemsrejects a sequel that is missing or not exactly one level harder. - Cards carry the sequel by page name (
data-sequel), keeping problem ids off the initial page. sequelSuggestioninweb/problem-picker.jsoffers the sequel when the latest interview by time passed and the sequel is not yet passed; fails and unscored interviews earn no offer.- Pressing "Practice it" selects the sequel across the level filters, moves focus to the random draw, and disables while an account refresh is pending; the selection stays unchanged until pressed.
Closes #536.