feat(joplin): add --joplin flag to fetch weekly timesheet note from Joplin
- Add joplin.py with fetch_week_note() that walks Work > Timesheets > YYYY and returns the body of the matching YYYY-WNN note via joppy ClientApi - Add filter_rows_by_date() to parser.py to extract only rows belonging to a specific day based on '# ... YYYY-MM-DD' headings in the document - Update cli.py: input and --joplin are now a mutually exclusive required group; add --token flag with JOPLIN_TOKEN env var fallback; --date is parsed into a real date object used for both output and day filtering - Add joppy as a runtime dependency (lazy-imported in cli.py) - Add tests for filter_rows_by_date and full mocked coverage of joplin.py - Update AGENTS.md with Joplin usage, notebook structure, and test rules The actual Joplin structure has notes directly inside the year notebook (Work > Timesheets > YYYY), not in per-week sub-notebooks as initially assumed. fetch_week_note() reflects this flat structure.
This commit is contained in:
parent
d6689a6c83
commit
ecdd28e8a3
8 changed files with 513 additions and 17 deletions
|
|
@ -7,6 +7,7 @@ from timesheets.parser import (
|
|||
build_description,
|
||||
detect_has_duration_column,
|
||||
extract_table_blocks,
|
||||
filter_rows_by_date,
|
||||
parse_document,
|
||||
parse_table,
|
||||
)
|
||||
|
|
@ -215,6 +216,67 @@ class TestParseDocument:
|
|||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# filter_rows_by_date
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFilterRowsByDate:
|
||||
# Reuse the W21 file which has one table per day-heading
|
||||
with open(WEEK_FILE, encoding="utf-8") as _f:
|
||||
_WEEK_LINES = _f.read().splitlines()
|
||||
|
||||
def test_returns_only_matching_day(self):
|
||||
from datetime import date
|
||||
|
||||
rows = filter_rows_by_date(self._WEEK_LINES, date(2026, 5, 22))
|
||||
assert len(rows) > 0
|
||||
# Friday has these projects in the sample file
|
||||
projects = {r["project"] for r in rows}
|
||||
assert "scrum" in projects
|
||||
|
||||
def test_different_day_returns_different_rows(self):
|
||||
from datetime import date
|
||||
|
||||
rows_fri = filter_rows_by_date(self._WEEK_LINES, date(2026, 5, 22))
|
||||
rows_mon = filter_rows_by_date(self._WEEK_LINES, date(2026, 5, 18))
|
||||
assert rows_fri != rows_mon
|
||||
assert len(rows_mon) > 0
|
||||
|
||||
def test_no_match_returns_empty(self):
|
||||
from datetime import date
|
||||
|
||||
rows = filter_rows_by_date(self._WEEK_LINES, date(2026, 1, 1))
|
||||
assert rows == []
|
||||
|
||||
def test_inline_document(self):
|
||||
from datetime import date
|
||||
|
||||
lines = [
|
||||
"# Maandag - 2026-05-18",
|
||||
"| Start | End | Project | Story | Note |",
|
||||
"|-------|-------|---------|-------|------||",
|
||||
"| 08:00 | 08:30 | bugs | | fix |",
|
||||
"",
|
||||
"# Dinsdag - 2026-05-19",
|
||||
"| Start | End | Project | Story | Note |",
|
||||
"|-------|-------|---------|-------|------||",
|
||||
"| 09:00 | 09:30 | scrum | | dsu |",
|
||||
]
|
||||
rows = filter_rows_by_date(lines, date(2026, 5, 18))
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["project"] == "bugs"
|
||||
|
||||
rows = filter_rows_by_date(lines, date(2026, 5, 19))
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["project"] == "scrum"
|
||||
|
||||
def test_empty_input(self):
|
||||
from datetime import date
|
||||
|
||||
assert filter_rows_by_date([], date(2026, 5, 22)) == []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# build_description
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue