Skip to content

Commit

Permalink
serialization logic for session rules
Browse files Browse the repository at this point in the history
This commit was sponsored by Quentin Pradet, Greg Back, Jason Mills,
and my other patrons.  If you want to join them, you can support my
work at https://glyph.im/patrons/.
  • Loading branch information
glyph committed Dec 10, 2024
1 parent 26bca39 commit 8274b8c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 3 deletions.
12 changes: 12 additions & 0 deletions src/pomodouroboros/model/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
"intervalType": Literal["Break"],
},
)
SavedTime = TypedDict("SavedTime",{"time": str, "zone": str})
SavedRule = TypedDict(
"SavedRule",
{
# TODO: these are obviously specifically-formatted strings, i.e. ISO
# formats for dailySart / dailyEnd, and weekday enums for days.
"dailyStart": SavedTime,
"dailyEnd": SavedTime,
"days": list[int],
},
)
SavedEvaluationResult = Literal[
"distracted", "interrupted", "focused", "achieved"
]
Expand Down Expand Up @@ -102,5 +113,6 @@
# scalability
"previousStreaks": list[SavedStreak],
"sessions": list[SavedSession],
"sessionRules": list[SavedRule],
},
)
38 changes: 37 additions & 1 deletion src/pomodouroboros/model/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@

from __future__ import annotations

from math import inf
from datetime import time
from functools import singledispatch
from json import dump, load
from math import inf
from os import makedirs, replace
from os.path import basename, dirname, exists, expanduser, join
from typing import Callable, TypeAlias, cast
from zoneinfo import ZoneInfo

from datetype import Time, aware
from fritter.boundaries import Scheduler
from fritter.drivers.memory import MemoryDriver
from fritter.scheduler import schedulerFromDriver

from pomodouroboros.model.intervals import Idle
from pomodouroboros.model.schema import SavedRule, SavedTime
from pomodouroboros.model.sessions import DailySessionRule, Weekday

from .boundaries import EvaluationResult, IntervalType, UserInterfaceFactory
from .intention import Estimate, Intention
Expand Down Expand Up @@ -114,6 +119,22 @@ def loadInterval(savedInterval: SavedInterval) -> AnyStreakInterval:
loadInterval(interval) for interval in saved["currentStreak"]
]

def loadRule(savedRule: SavedRule) -> DailySessionRule:

def loadOneTime(savedTime: SavedTime) -> Time[ZoneInfo]:
return aware(
time.fromisoformat(savedTime["time"]).replace(
tzinfo=ZoneInfo(savedTime["zone"])
),
ZoneInfo,
)

return DailySessionRule(
dailyStart=loadOneTime(savedRule["dailyStart"]),
dailyEnd=loadOneTime(savedRule["dailyEnd"]),
days={Weekday(each) for each in savedRule["days"]},
)

lastUpdateTime = saved["lastUpdateTime"]
scheduler: Scheduler[float, Callable[[], None], int] = schedulerFromDriver(
driver := MemoryDriver()
Expand Down Expand Up @@ -148,6 +169,7 @@ def loadInterval(savedInterval: SavedInterval) -> AnyStreakInterval:
_interfaceFactory=userInterfaceFactory,
_lastUpdateTime=lastUpdateTime,
_liveInterval=Idle(0, inf),
_sessionRules=[loadRule(rule) for rule in saved["sessionRules"]],
)
return nexus

Expand Down Expand Up @@ -250,6 +272,20 @@ def saveStartPrompt(interval: StartPrompt) -> SavedStartPrompt:
}
for session in nexus._sessions
],
"sessionRules": [
{
"dailyStart": {
"time": rule.dailyStart.isoformat(),
"zone": rule.dailyStart.tzinfo.key,
},
"dailyEnd": {
"time": rule.dailyEnd.isoformat(),
"zone": rule.dailyEnd.tzinfo.key,
},
"days": [day.value for day in rule.days],
}
for rule in nexus._sessionRules
],
}


Expand Down
25 changes: 23 additions & 2 deletions src/pomodouroboros/model/test/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
Pomodoro,
StartPrompt,
)
from ..nexus import Nexus
from ..nexus import Nexus, _noUIFactory
from ..observables import Changes, IgnoreChanges, SequenceObserver
from ..sessions import DailySessionRule, Session, Weekday
from ..storage import nexusFromJSON, nexusToJSON

TZ = ZoneInfo("America/Los_Angeles")


@dataclass
class TestInterval:
Expand Down Expand Up @@ -186,6 +188,7 @@ def setUp(self) -> None:
self.clock = Clock()
self.testUI = TestUserInterface(self.clock)
from math import inf

self.nexus = Nexus(
schedulerFromDriver(driver := MemoryDriver()),
driver,
Expand Down Expand Up @@ -401,7 +404,6 @@ def test_advanceToNewSession(self) -> None:
A nexus should start a new session automatically when its rules say
it's time to do that.
"""
TZ = ZoneInfo("America/Los_Angeles")
dailyStart = aware(
time(hour=9, minute=30, tzinfo=TZ),
ZoneInfo,
Expand Down Expand Up @@ -763,6 +765,25 @@ def test_story(self) -> None:
self.assertEqual(self.nexus._currentStreak, roundTrip._currentStreak)
self.assertEqual(self.nexus._sessions, roundTrip._sessions)

def test_saveSessionRules(self) -> None:
"""
Auto-starting session rules are persisted.
"""
dailyStart = aware(time(9, tzinfo=TZ), ZoneInfo)
dailyEnd = aware(time(5, tzinfo=TZ), ZoneInfo)
# TODO: replace this with L{ActiveSessionManager.rules}
self.nexus._sessionRules.append(
DailySessionRule(
dailyStart=dailyStart,
dailyEnd=dailyEnd,
days={Weekday.monday},
)
)
self.assertEqual(
self.nexus._sessionRules,
nexusFromJSON(nexusToJSON(self.nexus), _noUIFactory)._sessionRules,
)

def test_achievedEarly(self) -> None:
"""
If I achieve the desired intent of a pomodoro while it is still
Expand Down

0 comments on commit 8274b8c

Please sign in to comment.