Skip to content

Commit

Permalink
format with ruff
Browse files Browse the repository at this point in the history
  • Loading branch information
joshdavham committed Dec 7, 2024
1 parent 11ebbe8 commit 7011753
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 71 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import setuptools

setuptools.setup()
setuptools.setup()
2 changes: 1 addition & 1 deletion src/leitner_box/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
The classic Leitner System for Spaced Repetition, implemented as a python package.
"""

from .leitner_box import Scheduler, Card, Rating, ReviewLog
from .leitner_box import Scheduler, Card, Rating, ReviewLog
121 changes: 73 additions & 48 deletions src/leitner_box/leitner_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import Any, Literal
from copy import deepcopy


class Rating(IntEnum):
"""
Enum representing the two possible ratings when reviewing a Card object.
Expand All @@ -23,6 +24,7 @@ class Rating(IntEnum):
Fail = 0
Pass = 1


class Card:
"""
Represents a flashcard in the Leitner System.
Expand All @@ -37,8 +39,9 @@ class Card:
box: int
due: datetime | None

def __init__(self, card_id: int | None = None, box: int=1, due: datetime | None = None) -> None:

def __init__(
self, card_id: int | None = None, box: int = 1, due: datetime | None = None
) -> None:
if card_id is None:
card_id = int(datetime.now(timezone.utc).timestamp() * 1000)
self.card_id = card_id
Expand All @@ -47,34 +50,28 @@ def __init__(self, card_id: int | None = None, box: int=1, due: datetime | None
self.due = due

def to_dict(self) -> dict[str, int | str | None]:

return_dict: dict[str, int | str | None] = {
"card_id": self.card_id,
"box": self.box,
}

if self.due is not None:

return_dict["due"] = self.due.isoformat()

else:

return_dict["due"] = self.due

return return_dict

@staticmethod
def from_dict(source_dict: dict[str, Any]) -> "Card":
card_id = int(source_dict["card_id"])
box = int(source_dict["box"])

card_id = int(source_dict['card_id'])
box = int(source_dict['box'])

if source_dict['due'] is None:

due = source_dict['due']
if source_dict["due"] is None:
due = source_dict["due"]

else:

due = datetime.fromisoformat(source_dict["due"])

return Card(card_id=card_id, box=box, due=due)
Expand All @@ -83,7 +80,7 @@ def from_dict(source_dict: dict[str, Any]) -> "Card":
class ReviewLog:
"""
Represents the log entry of a Card object that has been reviewed.
Attributes:
card (Card): Copy of the card object that was reviewed.
rating (Rating): The rating given to the card during the review.
Expand All @@ -96,33 +93,42 @@ class ReviewLog:
review_datetime: datetime
review_duration: int | None

def __init__(self, card: Card, rating: Rating, review_datetime: datetime, review_duration: int | None = None) -> None:

def __init__(
self,
card: Card,
rating: Rating,
review_datetime: datetime,
review_duration: int | None = None,
) -> None:
self.card = deepcopy(card)
self.rating = rating
self.review_datetime = review_datetime
self.review_duration = review_duration

def to_dict(self) -> dict[str, dict[str, int | str | None] | int | str | None]:

return_dict = {
"card": self.card.to_dict(),
"rating": self.rating.value,
"review_datetime": self.review_datetime.isoformat(),
"review_duration": self.review_duration
"review_duration": self.review_duration,
}

return return_dict

@staticmethod
def from_dict(source_dict: dict[str, Any]) -> "ReviewLog":

card = Card.from_dict(source_dict['card'])
card = Card.from_dict(source_dict["card"])
rating = Rating(int(source_dict["rating"]))
review_datetime = datetime.fromisoformat(source_dict["review_datetime"])
review_duration = source_dict['review_duration']
review_duration = source_dict["review_duration"]

return ReviewLog(
card=card,
rating=rating,
review_datetime=review_datetime,
review_duration=review_duration,
)

return ReviewLog(card=card, rating=rating, review_datetime=review_datetime, review_duration=review_duration)

class Scheduler:
"""
Expand All @@ -140,13 +146,18 @@ class Scheduler:
start_datetime: datetime
on_fail: str

def __init__(self, box_intervals: list[int]=[1, 2, 7], start_datetime: datetime | None = None, on_fail: Literal['first_box', 'prev_box']='first_box') -> None:

def __init__(
self,
box_intervals: list[int] = [1, 2, 7],
start_datetime: datetime | None = None,
on_fail: Literal["first_box", "prev_box"] = "first_box",
) -> None:
if box_intervals[0] != 1:
raise ValueError(
"Box 1 must have an interval of 1 day. This may change in future versions."
)

raise ValueError("Box 1 must have an interval of 1 day. This may change in future versions.")

self.box_intervals = box_intervals # how many days in between you review each box; default box1 - everyday, box2 - every 2 days, box3, every seven days
self.box_intervals = box_intervals # how many days in between you review each box; default box1 - everyday, box2 - every 2 days, box3, every seven days
if start_datetime is None:
self.start_datetime = datetime.now()
else:
Expand All @@ -155,7 +166,13 @@ def __init__(self, box_intervals: list[int]=[1, 2, 7], start_datetime: datetime

self.on_fail = on_fail

def review_card(self, card: Card, rating: Rating, review_datetime: datetime | None = None, review_duration: int | None = None) -> tuple[Card, ReviewLog]:
def review_card(
self,
card: Card,
rating: Rating,
review_datetime: datetime | None = None,
review_duration: int | None = None,
) -> tuple[Card, ReviewLog]:
"""
Reviews a card with a given rating at a specified time.
Expand All @@ -175,61 +192,69 @@ def review_card(self, card: Card, rating: Rating, review_datetime: datetime | No
if review_datetime is None:
review_datetime = datetime.now()

review_log = ReviewLog(card=card, rating=rating, review_datetime=review_datetime, review_duration=review_duration)
review_log = ReviewLog(
card=card,
rating=rating,
review_datetime=review_datetime,
review_duration=review_duration,
)

review_datetime = review_datetime.replace(tzinfo=None) # review log datetimes can log timezone info, but it is dropped immediately after
review_datetime = review_datetime.replace(
tzinfo=None
) # review log datetimes can log timezone info, but it is dropped immediately after

# the card to be returned after review
new_card = deepcopy(card)

if new_card.due is None:
new_card.due = review_datetime.replace(hour=0, minute=0, second=0, microsecond=0) # beginning of the day of review
new_card.due = review_datetime.replace(
hour=0, minute=0, second=0, microsecond=0
) # beginning of the day of review

card_is_due = review_datetime >= new_card.due
if not card_is_due:
raise RuntimeError(f"Card is not due for review until {new_card.due}.")

if rating == Rating.Fail:

if self.on_fail == 'first_box':
if self.on_fail == "first_box":
new_card.box = 1
elif self.on_fail == 'prev_box' and new_card.box > 1:
elif self.on_fail == "prev_box" and new_card.box > 1:
new_card.box -= 1

elif rating == Rating.Pass:

if new_card.box < len(self.box_intervals):
new_card.box += 1

interval = self.box_intervals[new_card.box-1]
interval = self.box_intervals[new_card.box - 1]

begin_datetime = (self.start_datetime - timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
begin_datetime = (self.start_datetime - timedelta(days=1)).replace(
hour=0, minute=0, second=0, microsecond=0
)
i = 1
next_due_date = begin_datetime + (timedelta(days=interval) * i)
while next_due_date <= review_datetime:

next_due_date = begin_datetime + (timedelta(days=interval) * i)
i += 1

new_card.due = next_due_date

return new_card, review_log

def to_dict(self) -> dict[str, list[int] | int | str]:

def to_dict(self) -> dict[str, list[int] | int | str]:
return_dict: dict[str, list[int] | int | str] = {
"box_intervals": self.box_intervals,
"start_datetime": self.start_datetime.isoformat(),
"on_fail": self.on_fail
"on_fail": self.on_fail,
}

return return_dict

@staticmethod
def from_dict(source_dict: dict[str, Any]) -> "Scheduler":
box_intervals = source_dict["box_intervals"]
start_datetime = datetime.fromisoformat(source_dict["start_datetime"])
on_fail = source_dict["on_fail"]

box_intervals = source_dict['box_intervals']
start_datetime = datetime.fromisoformat(source_dict['start_datetime'])
on_fail = source_dict['on_fail']

return Scheduler(box_intervals=box_intervals, start_datetime=start_datetime, on_fail=on_fail)
return Scheduler(
box_intervals=box_intervals, start_datetime=start_datetime, on_fail=on_fail
)
Loading

0 comments on commit 7011753

Please sign in to comment.