Parcourir la source

Rename cal → store; extract _caldav.py; self-registering backends; StoreConfig

- room_cal/cal/ removed; replaced by room_cal/store/
- CalDAV implementation moved from cal/__init__ into store/_caldav.py
- store/_backend.py adds register_backend() decorator + _backend_factories registry
- CalDAVBackend and SQLiteBackend self-register via @register_backend and expose
  from_store_config(store_cfg, calendar_name) for factory construction
- Config.backend (flat string) replaced by Config.store: StoreConfig with nested
  caldav/sqlite sub-configs; store.active selects the active backend
- Calendar credentials now come from store.caldav config; caldav.env env-file
  fallback removed (no deployments to migrate)
- All import sites updated; store/__init__ re-exports Calendar/get_calendar for
  callers that need direct CalDAV access
ZiB il y a 4 mois
Parent
commit
20a54049a9

+ 1 - 1
room_cal/_health.py

@@ -5,7 +5,7 @@ import random
 from dataclasses import dataclass
 from datetime import datetime, timezone
 
-from room_cal.cal import Calendar, DEFAULT_CALENDAR_NAME
+from room_cal.store import Calendar, DEFAULT_CALENDAR_NAME
 from room_cal.service_api import ServiceAPI
 from room_cal.service_api.types import ServiceAuthError
 from room_cal._utils import services, local_today

+ 44 - 2
room_cal/_utils.py

@@ -331,6 +331,47 @@ class CalendarEntry:
     )
 
 
+@dataclass
+class CaldavStoreConfig:
+  url: str = ''
+  username: str = ''
+  password: str = ''
+  calendar_name: str = 'Room'
+
+  @classmethod
+  def from_dict(cls, d: dict) -> 'CaldavStoreConfig':
+    return cls(
+      url=d.get('url', ''),
+      username=d.get('username', ''),
+      password=d.get('password', ''),
+      calendar_name=d.get('calendar_name', 'Room'),
+    )
+
+
+@dataclass
+class SqliteStoreConfig:
+  db_path: str = ''
+
+  @classmethod
+  def from_dict(cls, d: dict) -> 'SqliteStoreConfig':
+    return cls(db_path=d.get('db_path', ''))
+
+
+@dataclass
+class StoreConfig:
+  active: str = 'caldav'
+  caldav: CaldavStoreConfig = field(default_factory=CaldavStoreConfig)
+  sqlite: SqliteStoreConfig = field(default_factory=SqliteStoreConfig)
+
+  @classmethod
+  def from_dict(cls, d: dict) -> 'StoreConfig':
+    return cls(
+      active=d.get('active', 'caldav'),
+      caldav=CaldavStoreConfig.from_dict(d.get('caldav', {})),
+      sqlite=SqliteStoreConfig.from_dict(d.get('sqlite', {})),
+    )
+
+
 @dataclass
 class Config:
   global_: GlobalConfig = field(default_factory=GlobalConfig)
@@ -338,17 +379,18 @@ class Config:
   rules: RulesConfig = field(default_factory=RulesConfig)
   calendars: list[CalendarEntry] = field(default_factory=list)
   api_url: str | None = None
-  backend: str = 'caldav'
+  store: StoreConfig = field(default_factory=StoreConfig)
 
   @classmethod
   def from_dict(cls, d: dict) -> 'Config':
+    store_d = d.get('store', {})
     return cls(
       global_=GlobalConfig.from_dict(d.get('global', {})),
       services={k: ServiceConfig.from_dict(k, v) for k, v in d.get('services', {}).items()},
       rules=RulesConfig.from_dict(d.get('rules', {})),
       calendars=[CalendarEntry.from_dict(e) for e in d.get('calendars', [])],
       api_url=d.get('api_url'),
-      backend=d.get('backend', 'caldav'),
+      store=StoreConfig.from_dict(store_d),
     )
 
 

+ 1 - 1
room_cal/actions/diff.py

@@ -2,7 +2,7 @@ import asyncio
 from datetime import date, timedelta
 from typing import TYPE_CHECKING
 
-from room_cal.cal import get_backend
+from room_cal.store import get_backend
 from room_cal.closed import Closed
 from room_cal.discount import Discount
 from room_cal.pricing.price_calendar import CalPrice

+ 1 - 1
room_cal/actions/push_prices.py

@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
 
 from datetime import datetime, timedelta
 
-from room_cal.cal import get_backend
+from room_cal.store import get_backend
 from room_cal._utils import safe_json_loads, filter_events, services as _services
 from room_cal.pricing.price_calendar import PriceData
 

+ 1 - 1
room_cal/api/_routes/closed.py

@@ -1,7 +1,7 @@
 from datetime import date
 from fastapi import APIRouter, Body
 from room_cal.closed import Closed, ClosedData
-from room_cal.cal import DEFAULT_CALENDAR_NAME
+from room_cal.store import DEFAULT_CALENDAR_NAME
 from room_cal._utils import safe_json_loads, iter_dates
 from room_cal.api._routes import API_SOURCE
 

+ 1 - 1
room_cal/api/_routes/discounts.py

@@ -1,7 +1,7 @@
 from datetime import date
 from fastapi import APIRouter, HTTPException, Body
 from room_cal.discount import Discount, DiscountData, DiscountType
-from room_cal.cal import DEFAULT_CALENDAR_NAME
+from room_cal.store import DEFAULT_CALENDAR_NAME
 from room_cal._utils import safe_json_loads, iter_dates
 from room_cal.api._routes import API_SOURCE
 

+ 1 - 1
room_cal/api/_routes/prices.py

@@ -1,7 +1,7 @@
 from datetime import date
 from fastapi import APIRouter, Body, HTTPException
 from room_cal.pricing import CalPrice
-from room_cal.cal import DEFAULT_CALENDAR_NAME
+from room_cal.store import DEFAULT_CALENDAR_NAME
 
 router = APIRouter()
 

+ 1 - 1
room_cal/api/_routes/reload.py

@@ -1,5 +1,5 @@
 from fastapi import APIRouter
-from room_cal.cal import clear_caches
+from room_cal.store import clear_caches
 
 router = APIRouter()
 

+ 1 - 1
room_cal/api/_routes/reservations.py

@@ -1,7 +1,7 @@
 from datetime import date
 from fastapi import APIRouter
 from room_cal.reservations import Reservation
-from room_cal.cal import DEFAULT_CALENDAR_NAME
+from room_cal.store import DEFAULT_CALENDAR_NAME
 from room_cal._utils import safe_json_loads, iter_dates
 
 router = APIRouter()

+ 1 - 1
room_cal/busy/__init__.py

@@ -1,5 +1,5 @@
 from datetime import date
-from room_cal.cal import StoreBacked
+from room_cal.store import StoreBacked
 from room_cal._utils import get_config
 
 DEFAULT_CALENDAR_NAME = 'Busy'

+ 1 - 1
room_cal/closed/__init__.py

@@ -1,7 +1,7 @@
 import json
 from dataclasses import dataclass, field
 from datetime import date
-from room_cal.cal import StoreBacked, DEFAULT_CALENDAR_NAME
+from room_cal.store import StoreBacked, DEFAULT_CALENDAR_NAME
 from room_cal._utils import EventSource, safe_json_loads, filter_events, services as _services, service_name, source_line
 
 _PREFIX = 'Closed: '

+ 12 - 1
room_cal/config-all.json

@@ -41,7 +41,18 @@
       }
     }
   },
-  "backend": "caldav",
+  "store": {
+    "active": "caldav",
+    "caldav": {
+      "url": "",
+      "username": "",
+      "password": "",
+      "calendar_name": "Room"
+    },
+    "sqlite": {
+      "db_path": ""
+    }
+  },
   "global": {
     "taxes": {
       "sales": 0.157

+ 1 - 1
room_cal/discount/__init__.py

@@ -2,7 +2,7 @@ import json
 from dataclasses import dataclass, field
 from datetime import date
 from enum import StrEnum
-from room_cal.cal import StoreBacked, DEFAULT_CALENDAR_NAME
+from room_cal.store import StoreBacked, DEFAULT_CALENDAR_NAME
 from room_cal._utils import safe_json_loads, filter_events, source_line
 
 _PREFIX = 'Discount: '

+ 2 - 2
room_cal/ical_feeds/__init__.py

@@ -2,7 +2,7 @@ import urllib.request
 from datetime import date, datetime, timedelta
 
 from room_cal._utils import CalendarEntry, EventSource, get_config
-from room_cal.cal._backend import EventRecord
+from room_cal.store._backend import EventRecord
 
 _source = EventSource('ical_feeds')
 
@@ -59,7 +59,7 @@ def _parse_ical(raw_bytes: bytes, calendar_name: str) -> list[EventRecord]:
 
 
 async def _fetch_caldav(entry: CalendarEntry) -> list[EventRecord]:
-  from room_cal.cal import Calendar
+  from room_cal.store import Calendar
 
   calendar_name = entry.caldav
   today = datetime.today()

+ 2 - 2
room_cal/mcp/__init__.py

@@ -2,7 +2,7 @@ import asyncio
 from datetime import date
 from fastmcp import FastMCP
 from room_cal._utils import services as _services, safe_json_loads, iter_dates
-from room_cal.cal import DEFAULT_CALENDAR_NAME
+from room_cal.store import DEFAULT_CALENDAR_NAME
 from room_cal._health import caldav_checks, credentials_checks
 from room_cal.pricing import CalPrice
 from room_cal.pricing.calc import (
@@ -168,7 +168,7 @@ async def delete_discount(target_date: str) -> None:
 @mcp.tool()
 async def reload_caches() -> dict:
   """Clear all in-memory CalDAV event caches so the next request re-fetches from the server. Returns {cleared_calendars}."""
-  from room_cal.cal import clear_caches
+  from room_cal.store import clear_caches
   count = clear_caches()
   return {'cleared_calendars': count}
 

+ 1 - 1
room_cal/pricing/price_calendar.py

@@ -1,7 +1,7 @@
 import json
 from dataclasses import dataclass, field
 from datetime import date
-from room_cal.cal import StoreBacked, DEFAULT_CALENDAR_NAME
+from room_cal.store import StoreBacked, DEFAULT_CALENDAR_NAME
 from room_cal._utils import safe_json_loads, filter_events, services as _services, get_config
 from room_cal.discount import DiscountType
 from . import calc

+ 1 - 1
room_cal/reservations/__init__.py

@@ -2,7 +2,7 @@ import json
 from dataclasses import dataclass, field
 from datetime import date, datetime
 from zoneinfo import ZoneInfo
-from room_cal.cal import StoreBacked, DEFAULT_CALENDAR_NAME
+from room_cal.store import StoreBacked, DEFAULT_CALENDAR_NAME
 from room_cal._utils import EventSource, safe_json_loads, filter_events, service_name, get_config
 from room_cal._mnemonic import wifi_key as _gen_wifi_key, door_code as _gen_door_code
 

+ 88 - 0
room_cal/store/__init__.py

@@ -0,0 +1,88 @@
+import uuid
+from datetime import date, datetime, timedelta
+
+from room_cal.store._backend import EventBackend, EventRecord
+from room_cal.store._caldav import Calendar, DEFAULT_CALENDAR_NAME, get_calendar
+from room_cal.store import _caldav, _sqlite  # trigger self-registration
+
+_backend_instances: dict[tuple[str, str], EventBackend] = {}
+
+
+def get_backend(name: str = DEFAULT_CALENDAR_NAME) -> EventBackend:
+  """Return the shared EventBackend for the given store name.
+
+  The backend type is determined by store.active in config.json
+  ('caldav' by default, 'sqlite' for local SQLite storage).
+  """
+  from room_cal._utils import get_config
+  from room_cal.store._backend import _backend_factories
+  cfg = get_config()
+  active = cfg.store.active
+  key = (active, name)
+  if key not in _backend_instances:
+    factory = _backend_factories.get(active)
+    if factory is None:
+      raise ValueError(f'Unknown store backend: {active!r}')
+    _backend_instances[key] = factory.from_store_config(cfg.store, name)
+  return _backend_instances[key]
+
+
+def clear_caches() -> int:
+  """Clear event caches for all active backend instances and raw Calendar objects.
+
+  Returns the total number of objects cleared.
+  """
+  cleared = 0
+  for backend in _backend_instances.values():
+    if hasattr(backend, 'clear_cache'):
+      backend.clear_cache()
+      cleared += 1
+  for cal in _caldav._calendar_registry.values():
+    cal._event_cache.clear()
+    cal._cached_url = None
+    cleared += 1
+  return cleared
+
+
+class StoreBacked:
+  """Base for objects backed by a single named event store."""
+  def __init__(self, calendar_name: str = DEFAULT_CALENDAR_NAME, *,
+               backend: 'EventBackend | None' = None):
+    self._calendar_name = calendar_name
+    self._backend: EventBackend = backend if backend is not None else get_backend(calendar_name)
+
+  async def _fetch_date_range(self, start_date: date, end_date: date) -> list[EventRecord]:
+    start = datetime(start_date.year, start_date.month, start_date.day)
+    end = datetime(end_date.year, end_date.month, end_date.day) + timedelta(days=1)
+    return await self._backend.fetch_range(start, end)
+
+  async def _create_all_day(self, start_date: date, title: str, description: str = '',
+                             x_data: str | None = None, *,
+                             end_date: date | None = None) -> EventRecord:
+    end = end_date if end_date is not None else start_date + timedelta(days=1)
+    record = EventRecord(
+      uid=str(uuid.uuid4()),
+      start=datetime(start_date.year, start_date.month, start_date.day),
+      end=datetime(end.year, end.month, end.day),
+      all_day=True,
+      title=title,
+      description=description,
+      x_data=x_data,
+    )
+    return await self._backend.create(record)
+
+  async def _create_timed(self, start: datetime, end: datetime, title: str,
+                           description: str = '', x_data: str | None = None) -> EventRecord:
+    record = EventRecord(
+      uid=str(uuid.uuid4()),
+      start=start,
+      end=end,
+      all_day=False,
+      title=title,
+      description=description,
+      x_data=x_data,
+    )
+    return await self._backend.create(record)
+
+
+CalendarBacked = StoreBacked  # backward compat alias

+ 1 - 1
room_cal/cal/__main__.py → room_cal/store/__main__.py

@@ -1,6 +1,6 @@
 import asyncio
 from datetime import datetime, timedelta
-from room_cal.cal import Calendar
+from room_cal.store._caldav import Calendar
 
 
 async def main():

+ 11 - 0
room_cal/cal/_backend.py → room_cal/store/_backend.py

@@ -21,3 +21,14 @@ class EventBackend(Protocol):
   async def update(self, uid: str, *, title: str | None = None,
                    description: str | None = None, x_data: str | None = None) -> EventRecord: ...
   async def delete(self, uid: str) -> None: ...
+
+
+_backend_factories: dict[str, type] = {}
+
+
+def register_backend(type_name: str):
+  """Class decorator that registers a backend under the given type name."""
+  def decorator(cls):
+    _backend_factories[type_name] = cls
+    return cls
+  return decorator

+ 24 - 105
room_cal/cal/__init__.py → room_cal/store/_caldav.py

@@ -1,28 +1,15 @@
 import asyncio
 import logging
-import os
 import uuid
 from caldav import aio
 from datetime import date, datetime, timedelta, timezone
 from icalendar import Calendar as _iCal, Event as _iEvent
 
-from room_cal._utils import data_home, read_env
-from room_cal.cal._backend import EventBackend, EventRecord
+from room_cal.store._backend import EventRecord, register_backend
 
 _log = logging.getLogger(__name__)
 
 
-def _caldav_config() -> dict[str, str]:
-  env_file = data_home() / 'caldav.env'
-  env = {**read_env(env_file), **os.environ}
-  url = env.get('CALDAV_URL', '')
-  username = env.get('CALDAV_USERNAME', '')
-  password = env.get('CALDAV_PASSWORD', '')
-  if not url:
-    raise ValueError(f'CALDAV_URL must be set in {env_file}')
-  return {'url': url, 'username': username, 'password': password}
-
-
 def _naive(dt) -> datetime:
   if isinstance(dt, datetime):
     return dt.replace(tzinfo=None)
@@ -54,12 +41,10 @@ def _parse_event(caldav_event) -> dict:
     'title':        str(vevent.get('SUMMARY', '')),
     'description':  str(vevent.get('DESCRIPTION', '')),
     'x_data':       x_data,
-    # sync fields
     'uid':          str(vevent.get('UID', '')),
     'url':          str(caldav_event.url),
     'etag':         getattr(caldav_event, 'etag', None),
     'recurrence_id': _naive(recurrence_id.dt) if recurrence_id else None,
-    # escape hatches
     '_vevent':      vevent,
     '_raw_event':   caldav_event,
   }
@@ -89,8 +74,9 @@ DEFAULT_CALENDAR_NAME = 'Room'
 
 
 class Calendar:
-  def __init__(self, calendar_name=DEFAULT_CALENDAR_NAME):
+  def __init__(self, calendar_name=DEFAULT_CALENDAR_NAME, *, url='', username='', password=''):
     self._calendar_name = calendar_name
+    self._connection = {'url': url, 'username': username, 'password': password}
     self._cached_url = None
     self._event_cache: dict[tuple, list] = {}
     self._inflight: dict[tuple, asyncio.Task] = {}
@@ -99,8 +85,13 @@ class Calendar:
   def calendar_name(self) -> str:
     return self._calendar_name
 
+  def _resolved_connection(self) -> dict:
+    if not self._connection.get('url'):
+      raise ValueError('CALDAV_URL must be set via store.caldav.url in config.json')
+    return self._connection
+
   async def _client(self):
-    return await aio.get_async_davclient(**_caldav_config())
+    return await aio.get_async_davclient(**self._resolved_connection())
 
   async def _get_calendar(self, client):
     if self._cached_url is not None:
@@ -127,10 +118,6 @@ class Calendar:
     return events
 
   async def fetch_date_range(self, start_date: date, end_date: date) -> list[dict]:
-    """Fetch events covering start_date through end_date (inclusive).
-
-    Results are cached; concurrent calls for the same range share one network request.
-    """
     key = (start_date, end_date)
     if key in self._event_cache:
       return self._event_cache[key]
@@ -207,25 +194,14 @@ class Calendar:
     return _parse_event(raw)
 
 
-_registry: dict[str, 'Calendar'] = {}
+_calendar_registry: dict[str, Calendar] = {}
 
 
-def get_calendar(name: str = DEFAULT_CALENDAR_NAME) -> 'Calendar':
+def get_calendar(name: str = DEFAULT_CALENDAR_NAME) -> Calendar:
   """Return the shared Calendar instance for the given name, creating it on first call."""
-  if name not in _registry:
-    _registry[name] = Calendar(name)
-  return _registry[name]
-
-
-def clear_caches() -> int:
-  """Clear event caches and cached URLs for all registered Calendar instances.
-
-  Returns the number of calendars cleared.
-  """
-  for cal in _registry.values():
-    cal._event_cache.clear()
-    cal._cached_url = None
-  return len(_registry)
+  if name not in _calendar_registry:
+    _calendar_registry[name] = Calendar(name)
+  return _calendar_registry[name]
 
 
 # ── CalDAVBackend ─────────────────────────────────────────────────────────────
@@ -243,10 +219,20 @@ def _dict_to_record(d: dict) -> EventRecord:
   )
 
 
+@register_backend('caldav')
 class CalDAVBackend:
   def __init__(self, cal: Calendar):
     self._cal = cal
 
+  @classmethod
+  def from_store_config(cls, store_cfg, calendar_name: str) -> 'CalDAVBackend':
+    cal_cfg = store_cfg.caldav
+    return cls(Calendar(calendar_name, url=cal_cfg.url, username=cal_cfg.username, password=cal_cfg.password))
+
+  def clear_cache(self):
+    self._cal._event_cache.clear()
+    self._cal._cached_url = None
+
   async def fetch_range(self, start: datetime, end: datetime) -> list[EventRecord]:
     events = await self._cal.fetch_events(start, end)
     return [_dict_to_record(e) for e in events]
@@ -301,70 +287,3 @@ class CalDAVBackend:
       await raw.delete()
     self._cal._event_cache.clear()
     _log.info('delete uid=%s', uid)
-
-
-# ── Backend registry + factory ────────────────────────────────────────────────
-
-_backend_registry: dict[str, CalDAVBackend] = {}
-
-
-def get_backend(name: str = DEFAULT_CALENDAR_NAME) -> EventBackend:
-  """Return the shared EventBackend for the given calendar name.
-
-  Uses the backend type configured via the 'backend' key in config.json
-  ('caldav' by default, 'sqlite' for local SQLite storage).
-  """
-  from room_cal._utils import get_config, data_home as _data_home
-  cfg = get_config()
-  if cfg.backend == 'sqlite':
-    from room_cal.cal._sqlite import SQLiteBackend
-    return SQLiteBackend(_data_home() / 'room_cal.db', name)
-  if name not in _backend_registry:
-    _backend_registry[name] = CalDAVBackend(get_calendar(name))
-  return _backend_registry[name]
-
-
-# ── StoreBacked ───────────────────────────────────────────────────────────────
-
-class StoreBacked:
-  """Base for objects backed by a single named event backend."""
-  def __init__(self, calendar_name: str = DEFAULT_CALENDAR_NAME, *,
-               backend: 'EventBackend | None' = None):
-    self._calendar_name = calendar_name
-    self._backend: EventBackend = backend if backend is not None else get_backend(calendar_name)
-
-  async def _fetch_date_range(self, start_date: date, end_date: date) -> list[EventRecord]:
-    start = datetime(start_date.year, start_date.month, start_date.day)
-    end = datetime(end_date.year, end_date.month, end_date.day) + timedelta(days=1)
-    return await self._backend.fetch_range(start, end)
-
-  async def _create_all_day(self, start_date: date, title: str, description: str = '',
-                             x_data: str | None = None, *,
-                             end_date: date | None = None) -> EventRecord:
-    end = end_date if end_date is not None else start_date + timedelta(days=1)
-    record = EventRecord(
-      uid=str(uuid.uuid4()),
-      start=datetime(start_date.year, start_date.month, start_date.day),
-      end=datetime(end.year, end.month, end.day),
-      all_day=True,
-      title=title,
-      description=description,
-      x_data=x_data,
-    )
-    return await self._backend.create(record)
-
-  async def _create_timed(self, start: datetime, end: datetime, title: str,
-                           description: str = '', x_data: str | None = None) -> EventRecord:
-    record = EventRecord(
-      uid=str(uuid.uuid4()),
-      start=start,
-      end=end,
-      all_day=False,
-      title=title,
-      description=description,
-      x_data=x_data,
-    )
-    return await self._backend.create(record)
-
-
-CalendarBacked = StoreBacked  # backward compat alias

+ 9 - 1
room_cal/cal/_sqlite.py → room_cal/store/_sqlite.py

@@ -2,7 +2,7 @@ import logging
 from datetime import datetime
 from pathlib import Path
 
-from room_cal.cal._backend import EventRecord
+from room_cal.store._backend import EventRecord, register_backend
 
 _log = logging.getLogger(__name__)
 
@@ -23,11 +23,19 @@ CREATE TABLE IF NOT EXISTS events (
 _COLS = 'uid, calendar_name, start, end, all_day, title, description, x_data'
 
 
+@register_backend('sqlite')
 class SQLiteBackend:
   def __init__(self, db_path: Path, calendar_name: str):
     self._db_path = db_path
     self._calendar_name = calendar_name
 
+  @classmethod
+  def from_store_config(cls, store_cfg, calendar_name: str) -> 'SQLiteBackend':
+    from room_cal._utils import data_home
+    db_path_str = store_cfg.sqlite.db_path
+    db_path = Path(db_path_str) if db_path_str else data_home() / 'store.db'
+    return cls(db_path, calendar_name)
+
   def _to_record(self, row) -> EventRecord:
     uid, _, start, end, all_day, title, description, x_data = row
     return EventRecord(

+ 1 - 1
room_cal/user_cal/__init__.py

@@ -1,6 +1,6 @@
 import json
 from datetime import date
-from room_cal.cal import StoreBacked
+from room_cal.store import StoreBacked
 from room_cal._utils import EventSource, safe_json_loads, services as _services, service_name
 
 DEFAULT_CALENDAR_NAME = 'House'

+ 1 - 1
room_cal/workflows/closed.py

@@ -3,7 +3,7 @@ from typing import Callable
 from fastapi import APIRouter, BackgroundTasks
 from pydantic import BaseModel
 
-from room_cal.cal import DEFAULT_CALENDAR_NAME
+from room_cal.store import DEFAULT_CALENDAR_NAME
 from room_cal.closed import Closed, ClosedData
 from room_cal.actions.fetch_closed import fetch_closed
 from room_cal.actions.push_closed import push_closed

+ 1 - 1
room_cal/workflows/prices.py

@@ -3,7 +3,7 @@ from typing import Callable, TYPE_CHECKING
 from fastapi import APIRouter, BackgroundTasks, HTTPException
 from pydantic import BaseModel
 
-from room_cal.cal import DEFAULT_CALENDAR_NAME
+from room_cal.store import DEFAULT_CALENDAR_NAME
 from room_cal.pricing import CalPrice
 from room_cal.discount import Discount, DiscountData, DiscountType
 from room_cal.actions.apply_discounts import apply_discounts

+ 1 - 1
scripts/patch_reservation_codes.py

@@ -12,7 +12,7 @@ import json
 import sys
 from datetime import date
 
-from room_cal.cal import Calendar, DEFAULT_CALENDAR_NAME
+from room_cal.store import Calendar, DEFAULT_CALENDAR_NAME
 from room_cal._utils import safe_json_loads, filter_events, service_name
 
 _PREFIX = 'Guest: '

+ 1 - 1
scripts/rename_source.py

@@ -29,7 +29,7 @@ from datetime import date
 
 sys.path.insert(0, str(__import__('pathlib').Path(__file__).parent.parent))
 
-from room_cal.cal import Calendar
+from room_cal.store import Calendar
 
 
 async def run(

+ 1 - 1
tests/test_api.py

@@ -111,7 +111,7 @@ async def test_action_dispatch_unknown_action(client):
 
 @pytest.mark.asyncio
 async def test_reload_clears_caches(client):
-  import room_cal.cal as _cal
+  import room_cal.store as _cal
   cal = _cal.get_calendar()
   cal._event_cache[('fake_key',)] = []
   cal._cached_url = 'http://example.com/cal'

+ 1 - 1
tests/test_ical_feeds.py

@@ -1,7 +1,7 @@
 import pytest
 from room_cal._utils import CalendarEntry
 from room_cal.ical_feeds import fetch_calendar
-from room_cal.cal._backend import EventRecord
+from room_cal.store._backend import EventRecord
 
 _FIRE_ENTRY = CalendarEntry(name='Fire', url='webcal://cal.4114.us/fire.ics')
 _HOUSE_ENTRY = CalendarEntry(name='House', caldav='House')

+ 1 - 1
tests/test_utils.py

@@ -2,7 +2,7 @@ import pytest
 from datetime import date, datetime, timedelta
 from pathlib import Path
 from room_cal._utils import safe_json_loads, iter_dates, event_dates, filter_events, read_env
-from room_cal.cal._backend import EventRecord
+from room_cal.store._backend import EventRecord
 
 
 # --- safe_json_loads ---