""" Joplin integration via the joppy ClientApi. Actual notebook structure: Work > Timesheets > YYYY Notes live directly inside the year notebook and are titled 'YYYY - WNN'. """ from datetime import date from typing import Optional from joppy.client_api import ClientApi def _iso_week_label(d: date) -> str: """Return the note title for the week containing the given date, e.g. '2026 - W21'.""" year, week, _ = d.isocalendar() return f"{year} - W{week:02d}" def _find_notebook( api: ClientApi, title: str, parent_id: Optional[str] = None ) -> Optional[str]: """Return the ID of a notebook matching title (and optionally parent_id), or None.""" for nb in api.get_all_notebooks(): if nb.title == title: if parent_id is None or nb.parent_id == parent_id: return nb.id return None def fetch_week_note(token: str, target_date: date) -> str: """ Fetch the body of the weekly timesheet note from Joplin for the week containing target_date. Notebook path: Work > Timesheets > YYYY Note title: YYYY - WNN Raises RuntimeError if any notebook or the note cannot be found. """ api = ClientApi(token=token) week_label = _iso_week_label(target_date) year_str = str(target_date.year) work_id = _find_notebook(api, "Work") if work_id is None: raise RuntimeError("Joplin notebook 'Work' not found") timesheets_id = _find_notebook(api, "Timesheets", parent_id=work_id) if timesheets_id is None: raise RuntimeError("Joplin notebook 'Work > Timesheets' not found") year_id = _find_notebook(api, year_str, parent_id=timesheets_id) if year_id is None: raise RuntimeError( f"Joplin notebook 'Work > Timesheets > {year_str}' not found" ) notes = api.get_all_notes(notebook_id=year_id, fields="id,title,body") for note in notes: if note.title == week_label: return note.body raise RuntimeError( f"Joplin note '{week_label}' not found in 'Work > Timesheets > {year_str}'" )