Skip to content

Commit 8274b8c

Browse files
committed
serialization logic for session rules
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/.
1 parent 26bca39 commit 8274b8c

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

src/pomodouroboros/model/schema.py

+12
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@
3737
"intervalType": Literal["Break"],
3838
},
3939
)
40+
SavedTime = TypedDict("SavedTime",{"time": str, "zone": str})
41+
SavedRule = TypedDict(
42+
"SavedRule",
43+
{
44+
# TODO: these are obviously specifically-formatted strings, i.e. ISO
45+
# formats for dailySart / dailyEnd, and weekday enums for days.
46+
"dailyStart": SavedTime,
47+
"dailyEnd": SavedTime,
48+
"days": list[int],
49+
},
50+
)
4051
SavedEvaluationResult = Literal[
4152
"distracted", "interrupted", "focused", "achieved"
4253
]
@@ -102,5 +113,6 @@
102113
# scalability
103114
"previousStreaks": list[SavedStreak],
104115
"sessions": list[SavedSession],
116+
"sessionRules": list[SavedRule],
105117
},
106118
)

src/pomodouroboros/model/storage.py

+37-1
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,23 @@
22

33
from __future__ import annotations
44

5-
from math import inf
5+
from datetime import time
66
from functools import singledispatch
77
from json import dump, load
8+
from math import inf
89
from os import makedirs, replace
910
from os.path import basename, dirname, exists, expanduser, join
1011
from typing import Callable, TypeAlias, cast
12+
from zoneinfo import ZoneInfo
1113

14+
from datetype import Time, aware
1215
from fritter.boundaries import Scheduler
1316
from fritter.drivers.memory import MemoryDriver
1417
from fritter.scheduler import schedulerFromDriver
1518

1619
from pomodouroboros.model.intervals import Idle
20+
from pomodouroboros.model.schema import SavedRule, SavedTime
21+
from pomodouroboros.model.sessions import DailySessionRule, Weekday
1722

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

122+
def loadRule(savedRule: SavedRule) -> DailySessionRule:
123+
124+
def loadOneTime(savedTime: SavedTime) -> Time[ZoneInfo]:
125+
return aware(
126+
time.fromisoformat(savedTime["time"]).replace(
127+
tzinfo=ZoneInfo(savedTime["zone"])
128+
),
129+
ZoneInfo,
130+
)
131+
132+
return DailySessionRule(
133+
dailyStart=loadOneTime(savedRule["dailyStart"]),
134+
dailyEnd=loadOneTime(savedRule["dailyEnd"]),
135+
days={Weekday(each) for each in savedRule["days"]},
136+
)
137+
117138
lastUpdateTime = saved["lastUpdateTime"]
118139
scheduler: Scheduler[float, Callable[[], None], int] = schedulerFromDriver(
119140
driver := MemoryDriver()
@@ -148,6 +169,7 @@ def loadInterval(savedInterval: SavedInterval) -> AnyStreakInterval:
148169
_interfaceFactory=userInterfaceFactory,
149170
_lastUpdateTime=lastUpdateTime,
150171
_liveInterval=Idle(0, inf),
172+
_sessionRules=[loadRule(rule) for rule in saved["sessionRules"]],
151173
)
152174
return nexus
153175

@@ -250,6 +272,20 @@ def saveStartPrompt(interval: StartPrompt) -> SavedStartPrompt:
250272
}
251273
for session in nexus._sessions
252274
],
275+
"sessionRules": [
276+
{
277+
"dailyStart": {
278+
"time": rule.dailyStart.isoformat(),
279+
"zone": rule.dailyStart.tzinfo.key,
280+
},
281+
"dailyEnd": {
282+
"time": rule.dailyEnd.isoformat(),
283+
"zone": rule.dailyEnd.tzinfo.key,
284+
},
285+
"days": [day.value for day in rule.days],
286+
}
287+
for rule in nexus._sessionRules
288+
],
253289
}
254290

255291

src/pomodouroboros/model/test/test_model.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
Pomodoro,
2626
StartPrompt,
2727
)
28-
from ..nexus import Nexus
28+
from ..nexus import Nexus, _noUIFactory
2929
from ..observables import Changes, IgnoreChanges, SequenceObserver
3030
from ..sessions import DailySessionRule, Session, Weekday
3131
from ..storage import nexusFromJSON, nexusToJSON
3232

33+
TZ = ZoneInfo("America/Los_Angeles")
34+
3335

3436
@dataclass
3537
class TestInterval:
@@ -186,6 +188,7 @@ def setUp(self) -> None:
186188
self.clock = Clock()
187189
self.testUI = TestUserInterface(self.clock)
188190
from math import inf
191+
189192
self.nexus = Nexus(
190193
schedulerFromDriver(driver := MemoryDriver()),
191194
driver,
@@ -401,7 +404,6 @@ def test_advanceToNewSession(self) -> None:
401404
A nexus should start a new session automatically when its rules say
402405
it's time to do that.
403406
"""
404-
TZ = ZoneInfo("America/Los_Angeles")
405407
dailyStart = aware(
406408
time(hour=9, minute=30, tzinfo=TZ),
407409
ZoneInfo,
@@ -763,6 +765,25 @@ def test_story(self) -> None:
763765
self.assertEqual(self.nexus._currentStreak, roundTrip._currentStreak)
764766
self.assertEqual(self.nexus._sessions, roundTrip._sessions)
765767

768+
def test_saveSessionRules(self) -> None:
769+
"""
770+
Auto-starting session rules are persisted.
771+
"""
772+
dailyStart = aware(time(9, tzinfo=TZ), ZoneInfo)
773+
dailyEnd = aware(time(5, tzinfo=TZ), ZoneInfo)
774+
# TODO: replace this with L{ActiveSessionManager.rules}
775+
self.nexus._sessionRules.append(
776+
DailySessionRule(
777+
dailyStart=dailyStart,
778+
dailyEnd=dailyEnd,
779+
days={Weekday.monday},
780+
)
781+
)
782+
self.assertEqual(
783+
self.nexus._sessionRules,
784+
nexusFromJSON(nexusToJSON(self.nexus), _noUIFactory)._sessionRules,
785+
)
786+
766787
def test_achievedEarly(self) -> None:
767788
"""
768789
If I achieve the desired intent of a pomodoro while it is still

0 commit comments

Comments
 (0)