Add real session-history navigation (Previous/Next/Shuffle), compact
side panels, and a segmented progress row to match the mockup layout.
Flashcards now opens straight into Study, with library management
moved into the Options menu ("Manage Flashcards") and Export hidden
from the UI (Import stays). Shuffle is backed by a real, due-before-new
randomized pick on the backend, not a decorative toggle.
387 lines
13 KiB
C#
387 lines
13 KiB
C#
using MongoDB.Bson;
|
|
using MongoDB.Bson.Serialization.Attributes;
|
|
|
|
namespace QuestionsApi.Models;
|
|
|
|
public static class FlashcardTypes
|
|
{
|
|
public const string Basic = "basic";
|
|
public const string Cloze = "cloze";
|
|
public static readonly HashSet<string> All = [Basic, Cloze];
|
|
}
|
|
|
|
public static class FlashcardRatings
|
|
{
|
|
public const string Again = "again";
|
|
public const string Hard = "hard";
|
|
public const string Good = "good";
|
|
public const string Easy = "easy";
|
|
public static readonly string[] All = [Again, Hard, Good, Easy];
|
|
}
|
|
|
|
public static class FlashcardScheduleStates
|
|
{
|
|
public const string New = "new";
|
|
public const string Learning = "learning";
|
|
public const string Review = "review";
|
|
public const string Relearning = "relearning";
|
|
}
|
|
|
|
public sealed class FlashcardDeck
|
|
{
|
|
[BsonId, BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; }
|
|
public string UserId { get; set; } = "";
|
|
public string StableId { get; set; } = Guid.NewGuid().ToString();
|
|
public string Name { get; set; } = "";
|
|
public string NormalizedName { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public bool IsDefault { get; set; }
|
|
public int? NewCardsPerDay { get; set; }
|
|
public int? ReviewsPerDay { get; set; }
|
|
public double? DesiredRetention { get; set; }
|
|
public long Revision { get; set; } = 1;
|
|
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
|
|
public DateTime UpdatedUtc { get; set; } = DateTime.UtcNow;
|
|
public DateTime? ArchivedUtc { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardImageReference
|
|
{
|
|
public string Kind { get; set; } = "owned";
|
|
public string AssetId { get; set; } = "";
|
|
}
|
|
|
|
public sealed class FlashcardNote
|
|
{
|
|
[BsonId, BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; }
|
|
public string UserId { get; set; } = "";
|
|
public string StableId { get; set; } = Guid.NewGuid().ToString();
|
|
public string DeckId { get; set; } = "";
|
|
public string Type { get; set; } = FlashcardTypes.Basic;
|
|
public string Prompt { get; set; } = "";
|
|
public string Answer { get; set; } = "";
|
|
public string Extra { get; set; } = "";
|
|
public List<string> Tags { get; set; } = [];
|
|
public List<FlashcardImageReference> Images { get; set; } = [];
|
|
public string SourceQuestionExternalId { get; set; } = "";
|
|
public string ContentFingerprint { get; set; } = "";
|
|
public long Revision { get; set; } = 1;
|
|
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
|
|
public DateTime UpdatedUtc { get; set; } = DateTime.UtcNow;
|
|
public DateTime? ArchivedUtc { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardSchedule
|
|
{
|
|
[BsonId, BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; }
|
|
public string UserId { get; set; } = "";
|
|
public string NoteId { get; set; } = "";
|
|
public int Variant { get; set; }
|
|
public string State { get; set; } = FlashcardScheduleStates.New;
|
|
public int? Step { get; set; }
|
|
public double? Stability { get; set; }
|
|
public double? Difficulty { get; set; }
|
|
public DateTime DueUtc { get; set; } = DateTime.UtcNow;
|
|
public DateTime? LastReviewUtc { get; set; }
|
|
public DateTime? BuriedUntilUtc { get; set; }
|
|
public bool IsSuspended { get; set; }
|
|
public long Revision { get; set; } = 1;
|
|
public string AlgorithmVersion { get; set; } = "fsrs-core-1.0.7";
|
|
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public sealed class FlashcardReviewLog
|
|
{
|
|
[BsonId] public string Id { get; set; } = "";
|
|
public string UserId { get; set; } = "";
|
|
public string CardId { get; set; } = "";
|
|
public string Rating { get; set; } = "";
|
|
public DateTime ReviewedUtc { get; set; }
|
|
public int DurationSeconds { get; set; }
|
|
public FlashcardScheduleSnapshot Before { get; set; } = new();
|
|
public FlashcardScheduleSnapshot After { get; set; } = new();
|
|
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
|
|
public DateTime? UndoneUtc { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardScheduleSnapshot
|
|
{
|
|
public string State { get; set; } = FlashcardScheduleStates.New;
|
|
public int? Step { get; set; }
|
|
public double? Stability { get; set; }
|
|
public double? Difficulty { get; set; }
|
|
public DateTime DueUtc { get; set; }
|
|
public DateTime? LastReviewUtc { get; set; }
|
|
public long Revision { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardMediaAsset
|
|
{
|
|
[BsonId, BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; }
|
|
public string UserId { get; set; } = "";
|
|
public string FileName { get; set; } = "";
|
|
public string ContentType { get; set; } = "";
|
|
public byte[] Bytes { get; set; } = [];
|
|
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public sealed class FlashcardSettings
|
|
{
|
|
[BsonId, BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; }
|
|
public string UserId { get; set; } = "";
|
|
public int NewCardsPerDay { get; set; } = 20;
|
|
public int ReviewsPerDay { get; set; } = 200;
|
|
public double DesiredRetention { get; set; } = 0.90;
|
|
public long Revision { get; set; } = 1;
|
|
public DateTime UpdatedUtc { get; set; } = DateTime.UtcNow;
|
|
}
|
|
|
|
public sealed class UpsertFlashcardDeckRequest
|
|
{
|
|
public string Name { get; set; } = "";
|
|
public string Description { get; set; } = "";
|
|
public int? NewCardsPerDay { get; set; }
|
|
public int? ReviewsPerDay { get; set; }
|
|
public double? DesiredRetention { get; set; }
|
|
public long? ExpectedRevision { get; set; }
|
|
}
|
|
|
|
public sealed class UpsertFlashcardNoteRequest
|
|
{
|
|
public string DeckId { get; set; } = "";
|
|
public string StableId { get; set; } = "";
|
|
public string Type { get; set; } = FlashcardTypes.Basic;
|
|
public string Prompt { get; set; } = "";
|
|
public string Answer { get; set; } = "";
|
|
public string Extra { get; set; } = "";
|
|
public List<string> Tags { get; set; } = [];
|
|
public List<FlashcardImageReference> Images { get; set; } = [];
|
|
public string SourceQuestionExternalId { get; set; } = "";
|
|
public long? ExpectedRevision { get; set; }
|
|
}
|
|
|
|
public sealed class UpdateFlashcardSettingsRequest
|
|
{
|
|
public int NewCardsPerDay { get; set; } = 20;
|
|
public int ReviewsPerDay { get; set; } = 200;
|
|
public double DesiredRetention { get; set; } = 0.90;
|
|
public long? ExpectedRevision { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardDeckSummary
|
|
{
|
|
public FlashcardDeck Deck { get; set; } = new();
|
|
public int Total { get; set; }
|
|
public int Due { get; set; }
|
|
public int New { get; set; }
|
|
public int Suspended { get; set; }
|
|
public int Learning { get; set; }
|
|
public int Review { get; set; }
|
|
public int Mastered { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardLibraryResponse
|
|
{
|
|
public List<FlashcardNote> Items { get; set; } = [];
|
|
public int Total { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardLibraryQuery
|
|
{
|
|
public string? DeckId { get; set; }
|
|
public string? Tag { get; set; }
|
|
public string? Type { get; set; }
|
|
public string? State { get; set; }
|
|
public string? Search { get; set; }
|
|
public int Page { get; set; } = 1;
|
|
public int PageSize { get; set; } = 50;
|
|
}
|
|
|
|
public sealed class FlashcardStudyRequest
|
|
{
|
|
public string DeckId { get; set; } = "";
|
|
public List<string> ExcludeCardIds { get; set; } = [];
|
|
public DateTime StudyDayStartUtc { get; set; } = DateTime.UtcNow.Date;
|
|
public bool Shuffle { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardBulkNoteIdsRequest
|
|
{
|
|
public List<string> NoteIds { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardMoveRequest
|
|
{
|
|
public List<string> NoteIds { get; set; } = [];
|
|
public string TargetDeckId { get; set; } = "";
|
|
}
|
|
|
|
public sealed class FlashcardBatchReviewRequest
|
|
{
|
|
public List<FlashcardReviewRequest> Reviews { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardBatchReviewResponse
|
|
{
|
|
public List<FlashcardReviewResponse> Results { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardUndoRequest
|
|
{
|
|
public string ReviewLogId { get; set; } = "";
|
|
}
|
|
|
|
public sealed class FlashcardRatingOption
|
|
{
|
|
public string Rating { get; set; } = "";
|
|
public string IntervalLabel { get; set; } = "";
|
|
public DateTime DueUtc { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardStudyCardResponse
|
|
{
|
|
public FlashcardNote? Note { get; set; }
|
|
public FlashcardSchedule? Card { get; set; }
|
|
public List<FlashcardRatingOption> RatingOptions { get; set; } = [];
|
|
public int DueRemaining { get; set; }
|
|
public int NewRemaining { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardReviewRequest
|
|
{
|
|
public string ClientReviewId { get; set; } = "";
|
|
public string CardId { get; set; } = "";
|
|
public string Rating { get; set; } = "";
|
|
public long ExpectedRevision { get; set; }
|
|
public DateTime ReviewedUtc { get; set; }
|
|
public int DurationSeconds { get; set; }
|
|
public DateTime StudyDayStartUtc { get; set; } = DateTime.UtcNow.Date;
|
|
}
|
|
|
|
public sealed class FlashcardReviewResponse
|
|
{
|
|
public FlashcardSchedule Card { get; set; } = new();
|
|
public List<FlashcardRatingOption> RatingOptions { get; set; } = [];
|
|
public bool Duplicate { get; set; }
|
|
}
|
|
|
|
public sealed class FlashcardDashboardCounts
|
|
{
|
|
public int Due { get; set; }
|
|
public int NewAvailable { get; set; }
|
|
public int ReviewedThisWeek { get; set; }
|
|
public int Learning { get; set; }
|
|
public int Review { get; set; }
|
|
public int CurrentStreakDays { get; set; }
|
|
public int ReviewsToday { get; set; }
|
|
public int DailyGoal { get; set; }
|
|
}
|
|
|
|
public static class FlashcardImportFormats
|
|
{
|
|
public const string Csv = "csv";
|
|
public const string Json = "json";
|
|
}
|
|
|
|
public static class FlashcardImportRowActions
|
|
{
|
|
public const string Create = "create";
|
|
public const string Update = "update";
|
|
public const string Skip = "skip";
|
|
public const string Error = "error";
|
|
}
|
|
|
|
public sealed class FlashcardImportPendingRecord
|
|
{
|
|
public int RowNumber { get; set; }
|
|
public string Action { get; set; } = FlashcardImportRowActions.Create;
|
|
public string? Error { get; set; }
|
|
public string StableId { get; set; } = "";
|
|
public string DeckName { get; set; } = "";
|
|
public string Type { get; set; } = FlashcardTypes.Basic;
|
|
public string Prompt { get; set; } = "";
|
|
public string Answer { get; set; } = "";
|
|
public string Extra { get; set; } = "";
|
|
public List<string> Tags { get; set; } = [];
|
|
public string SourceQuestionExternalId { get; set; } = "";
|
|
public List<FlashcardSchedule> Cards { get; set; } = [];
|
|
public List<FlashcardReviewLog> ReviewLogs { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardImportPreview
|
|
{
|
|
[BsonId] public string Id { get; set; } = "";
|
|
public string UserId { get; set; } = "";
|
|
public string Format { get; set; } = FlashcardImportFormats.Csv;
|
|
public List<FlashcardImportPendingRecord> Records { get; set; } = [];
|
|
public DateTime CreatedUtc { get; set; } = DateTime.UtcNow;
|
|
public DateTime ExpiresUtc { get; set; } = DateTime.UtcNow.AddHours(1);
|
|
}
|
|
|
|
public sealed class FlashcardImportPreviewRow
|
|
{
|
|
public int RowNumber { get; set; }
|
|
public string Action { get; set; } = FlashcardImportRowActions.Create;
|
|
public string? Error { get; set; }
|
|
public string StableId { get; set; } = "";
|
|
public string Deck { get; set; } = "";
|
|
public string Type { get; set; } = FlashcardTypes.Basic;
|
|
public string Prompt { get; set; } = "";
|
|
public string Answer { get; set; } = "";
|
|
}
|
|
|
|
public sealed class FlashcardImportPreviewResponse
|
|
{
|
|
public string ImportToken { get; set; } = "";
|
|
public int CreateCount { get; set; }
|
|
public int UpdateCount { get; set; }
|
|
public int SkipCount { get; set; }
|
|
public int ErrorCount { get; set; }
|
|
public List<FlashcardImportPreviewRow> Rows { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardImportCommitRequest
|
|
{
|
|
public string ImportToken { get; set; } = "";
|
|
}
|
|
|
|
public sealed class FlashcardImportCommitResponse
|
|
{
|
|
public int Created { get; set; }
|
|
public int Updated { get; set; }
|
|
public int Skipped { get; set; }
|
|
public int Errored { get; set; }
|
|
public List<FlashcardImportPreviewRow> Errors { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardExportOmission
|
|
{
|
|
public string Kind { get; set; } = "";
|
|
public string Reason { get; set; } = "";
|
|
}
|
|
|
|
public sealed class FlashcardJsonExport
|
|
{
|
|
public int SchemaVersion { get; set; } = 1;
|
|
public DateTime ExportedUtc { get; set; } = DateTime.UtcNow;
|
|
public FlashcardSettings Settings { get; set; } = new();
|
|
public List<FlashcardDeck> Decks { get; set; } = [];
|
|
public List<FlashcardJsonExportNote> Notes { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardJsonExportNote
|
|
{
|
|
public FlashcardNote Note { get; set; } = new();
|
|
public List<FlashcardSchedule> Cards { get; set; } = [];
|
|
public List<FlashcardReviewLog> ReviewLogs { get; set; } = [];
|
|
public List<FlashcardJsonExportImage> Images { get; set; } = [];
|
|
}
|
|
|
|
public sealed class FlashcardJsonExportImage
|
|
{
|
|
public string AssetId { get; set; } = "";
|
|
public string FileName { get; set; } = "";
|
|
public string ContentType { get; set; } = "";
|
|
public string Base64Bytes { get; set; } = "";
|
|
}
|