feat(studyset): prepare for an exam, with plans, a date and the pace to it

A studyset plans for exams: each plan names what an exam covers, the
topics it takes whole (with their flashcards, learning paths and PDFs)
and the tags whose flashcards it takes from anywhere in the studyset.
The author's plans are published with the studyset (payload schema 7,
`examPlans`); copying a release and taking a newer one point their
topics at the copy's own studysets. Plans are also offered from level
tags: JLPT N5 to N1, HSK 1 to 7-9 and KRDict's levels, each taking the
levels below it, as the exams do.

The person chooses a plan and the exam's day (`exam`), which stays
theirs: the server leaves it out of a release, as a PDF's deadline.
The study plan then shows the days left, how many of the plan's
flashcards were started, its paths' steps done and its PDFs' pages
read, and the pace that gets everything new learnt a week before the
exam (by the exam when two weeks or fewer are left): new flashcards a
day, steps a week, pages a day. Study for the exam studies only what
the plan covers, its new cards at that pace (counted as the day began)
and every review due.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-14 14:26:08 +02:00
commit dd6c567ad0
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -10563,6 +10563,186 @@ abstract class AppLocalizations {
/// **'Learning paths'**
String get planLearningPaths;
/// No description provided for @examPrepare.
///
/// In en, this message translates to:
/// **'Prepare for an exam'**
String get examPrepare;
/// No description provided for @examPrepareHint.
///
/// In en, this message translates to:
/// **'Choose what it covers and its date, and get a pace that gets you there.'**
String get examPrepareHint;
/// No description provided for @examChange.
///
/// In en, this message translates to:
/// **'Change the exam'**
String get examChange;
/// No description provided for @examDaysLeft.
///
/// In en, this message translates to:
/// **'{count, plural, =0{Today, {date}} =1{Tomorrow, {date}} other{In {count} days, {date}}}'**
String examDaysLeft(int count, String date);
/// No description provided for @examPast.
///
/// In en, this message translates to:
/// **'It was on {date}'**
String examPast(String date);
/// No description provided for @examPlanGone.
///
/// In en, this message translates to:
/// **'This exam plan is gone'**
String get examPlanGone;
/// No description provided for @examCardsStarted.
///
/// In en, this message translates to:
/// **'{total, plural, =1{{started} of 1 flashcard started} other{{started} of {total} flashcards started}}'**
String examCardsStarted(int started, int total);
/// No description provided for @examPagesRead.
///
/// In en, this message translates to:
/// **'{total, plural, =1{{read} of 1 page read} other{{read} of {total} pages read}}'**
String examPagesRead(int read, int total);
/// No description provided for @examPaceTitle.
///
/// In en, this message translates to:
/// **'Your pace'**
String get examPaceTitle;
/// No description provided for @examPaceNew.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 new flashcard a day} other{{count} new flashcards a day}}'**
String examPaceNew(int count);
/// No description provided for @examPaceSteps.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 step a week} other{{count} steps a week}}'**
String examPaceSteps(int count);
/// No description provided for @examPacePages.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 page a day} other{{count} pages a day}}'**
String examPacePages(int count);
/// No description provided for @examPaceDone.
///
/// In en, this message translates to:
/// **'Everything new is learnt: review until the exam.'**
String get examPaceDone;
/// No description provided for @examStudy.
///
/// In en, this message translates to:
/// **'Study for the exam'**
String get examStudy;
/// No description provided for @examSheetHelp.
///
/// In en, this message translates to:
/// **'Choose what the exam covers and when it is. Everything new is then spread so that it is learnt a week before, leaving the last week for reviews. Only you see the date.'**
String get examSheetHelp;
/// No description provided for @examDate.
///
/// In en, this message translates to:
/// **'Exam date'**
String get examDate;
/// No description provided for @examSheetPlans.
///
/// In en, this message translates to:
/// **'This studyset’s exam plans'**
String get examSheetPlans;
/// No description provided for @examSheetFromTags.
///
/// In en, this message translates to:
/// **'From the level tags'**
String get examSheetFromTags;
/// No description provided for @examSheetNoPlans.
///
/// In en, this message translates to:
/// **'No exam plans yet. Make one from this studyset’s topics and tags.'**
String get examSheetNoPlans;
/// No description provided for @examRemove.
///
/// In en, this message translates to:
/// **'Stop preparing'**
String get examRemove;
/// No description provided for @examPlanTopicCount.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 topic} other{{count} topics}}'**
String examPlanTopicCount(int count);
/// No description provided for @examPlanNew.
///
/// In en, this message translates to:
/// **'New exam plan'**
String get examPlanNew;
/// No description provided for @examPlanEdit.
///
/// In en, this message translates to:
/// **'Edit exam plan'**
String get examPlanEdit;
/// No description provided for @examPlanHelp.
///
/// In en, this message translates to:
/// **'An exam plan names what an exam covers: whole topics, with their flashcards, learning paths and PDFs, and flashcards with certain tags. It is published with the studyset.'**
String get examPlanHelp;
/// No description provided for @examPlanName.
///
/// In en, this message translates to:
/// **'Name'**
String get examPlanName;
/// No description provided for @examPlanNameHint.
///
/// In en, this message translates to:
/// **'e.g. Final exam'**
String get examPlanNameHint;
/// No description provided for @examPlanTopics.
///
/// In en, this message translates to:
/// **'Topics it covers whole'**
String get examPlanTopics;
/// No description provided for @examPlanTags.
///
/// In en, this message translates to:
/// **'Flashcards with these tags'**
String get examPlanTags;
/// No description provided for @examPlanEmpty.
///
/// In en, this message translates to:
/// **'This studyset has no topics or tagged flashcards to choose from yet.'**
String get examPlanEmpty;
/// No description provided for @examPlanDelete.
///
/// In en, this message translates to:
/// **'Delete plan'**
String get examPlanDelete;
/// No description provided for @jellyfinTitle.
///
/// In en, this message translates to: