You've already forked HeurAMS-Classic
168 lines
4.7 KiB
Python
168 lines
4.7 KiB
Python
"""
|
|
fsrs.card
|
|
---------
|
|
|
|
This module defines the Card and State classes.
|
|
|
|
Classes:
|
|
Card: Represents a flashcard in the FSRS system.
|
|
State: Enum representing the learning state of a Card object.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
import time
|
|
import json
|
|
from typing import TypedDict
|
|
from typing_extensions import Self
|
|
from fsrs.state import State
|
|
|
|
|
|
class CardDict(TypedDict):
|
|
"""
|
|
JSON-serializable dictionary representation of a Card object.
|
|
"""
|
|
|
|
card_id: int
|
|
state: int
|
|
step: int | None
|
|
stability: float | None
|
|
difficulty: float | None
|
|
due: str
|
|
last_review: str | None
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Card:
|
|
"""
|
|
Represents a flashcard in the FSRS system.
|
|
|
|
Attributes:
|
|
card_id: The id of the card. Defaults to the epoch milliseconds of when the card was created.
|
|
state: The card's current learning state.
|
|
step: The card's current learning or relearning step or None if the card is in the Review state.
|
|
stability: Core mathematical parameter used for future scheduling.
|
|
difficulty: Core mathematical parameter used for future scheduling.
|
|
due: The date and time when the card is due next.
|
|
last_review: The date and time of the card's last review.
|
|
"""
|
|
|
|
card_id: int
|
|
state: State
|
|
step: int | None
|
|
stability: float | None
|
|
difficulty: float | None
|
|
due: datetime
|
|
last_review: datetime | None
|
|
|
|
def __init__(
|
|
self,
|
|
card_id: int | None = None,
|
|
state: State = State.Learning,
|
|
step: int | None = None,
|
|
stability: float | None = None,
|
|
difficulty: float | None = None,
|
|
due: datetime | None = None,
|
|
last_review: datetime | None = None,
|
|
) -> None:
|
|
if card_id is None:
|
|
# epoch milliseconds of when the card was created
|
|
card_id = int(datetime.now(timezone.utc).timestamp() * 1000)
|
|
# wait 1ms to prevent potential card_id collision on next Card creation
|
|
time.sleep(0.001)
|
|
self.card_id = card_id
|
|
|
|
self.state = state
|
|
|
|
if self.state == State.Learning and step is None:
|
|
step = 0
|
|
self.step = step
|
|
|
|
self.stability = stability
|
|
self.difficulty = difficulty
|
|
|
|
if due is None:
|
|
due = datetime.now(timezone.utc)
|
|
self.due = due
|
|
|
|
self.last_review = last_review
|
|
|
|
def to_dict(self) -> CardDict:
|
|
"""
|
|
Returns a dictionary representation of the Card object.
|
|
|
|
Returns:
|
|
CardDict: A dictionary representation of the Card object.
|
|
"""
|
|
|
|
return {
|
|
"card_id": self.card_id,
|
|
"state": self.state.value,
|
|
"step": self.step,
|
|
"stability": self.stability,
|
|
"difficulty": self.difficulty,
|
|
"due": self.due.isoformat(),
|
|
"last_review": self.last_review.isoformat() if self.last_review else None,
|
|
}
|
|
|
|
@classmethod
|
|
def from_dict(cls, source_dict: CardDict) -> Self:
|
|
"""
|
|
Creates a Card object from an existing dictionary.
|
|
|
|
Args:
|
|
source_dict: A dictionary representing an existing Card object.
|
|
|
|
Returns:
|
|
Self: A Card object created from the provided dictionary.
|
|
"""
|
|
|
|
return cls(
|
|
card_id=int(source_dict["card_id"]),
|
|
state=State(int(source_dict["state"])),
|
|
step=source_dict["step"],
|
|
stability=(
|
|
float(source_dict["stability"]) if source_dict["stability"] else None
|
|
),
|
|
difficulty=(
|
|
float(source_dict["difficulty"]) if source_dict["difficulty"] else None
|
|
),
|
|
due=datetime.fromisoformat(source_dict["due"]),
|
|
last_review=(
|
|
datetime.fromisoformat(source_dict["last_review"])
|
|
if source_dict["last_review"]
|
|
else None
|
|
),
|
|
)
|
|
|
|
def to_json(self, indent: int | str | None = None) -> str:
|
|
"""
|
|
Returns a JSON-serialized string of the Card object.
|
|
|
|
Args:
|
|
indent: Equivalent argument to the indent in json.dumps()
|
|
|
|
Returns:
|
|
str: A JSON-serialized string of the Card object.
|
|
"""
|
|
return json.dumps(self.to_dict(), indent=indent)
|
|
|
|
@classmethod
|
|
def from_json(cls, source_json: str) -> Self:
|
|
"""
|
|
Creates a Card object from a JSON-serialized string.
|
|
|
|
Args:
|
|
source_json: A JSON-serialized string of an existing Card object.
|
|
|
|
Returns:
|
|
Self: A Card object created from the JSON string.
|
|
"""
|
|
|
|
source_dict: CardDict = json.loads(source_json)
|
|
return cls.from_dict(source_dict=source_dict)
|
|
|
|
|
|
__all__ = ["Card"]
|