Turn a weekly org-mode block table into a dated schedule, an iCalendar file, and a printable time-block PDF.
Project description
writing-schedule (Python)
A Python port of mooerslab/writing-schedule.el. It reads the same weekly org-mode
block table and produces the same three outputs, namely the dated schedule
.org file, an iCalendar .ics file, and a printable time-block PDF. A
person who cannot install Emacs gets the same artifacts from a single
pip install, because the PDF is drawn with ReportLab rather than TeX.
The org table stays the planning surface, because it is plain text that any
editor can handle. The two implementations are two performances of one score:
the format specification in formatspec.org is the score, the elisp package
is the reference recording, and a shared fixture directory keeps the Python
variant from drifting into a dialect of its own.
Installation
pip install writing-schedule # once published to PyPI
pipx install writing-schedule # isolated console tool
pip install -e . # from a checkout, for development
The only runtime dependency is ReportLab. The iCalendar writer and the
parser use the standard library alone, including zoneinfo for daylight
saving.
Command line
The subcommands mirror the shell front end of the elisp package.
writing-schedule generate TABLE --week 2026-01-19 [--dir OUT] [--no-ics] [--no-todo] [--tz ZONE]
writing-schedule export TABLE --week 2026-01-19 [--dir OUT] [--tz ZONE]
writing-schedule sheets TABLE --week 2026-01-19 [--dir OUT] [--engine reportlab|latex] [--format pdf|org|both] [--per-day]
writing-schedule template N [--out FILE]
writing-schedule weeks [--dir OUT]
--week takes any date inside the target week, because the week snaps to the
Monday on or before that date. Examples:
writing-schedule generate projects-and-tasks.org --week 2026-01-19 --dir out
writing-schedule sheets projects-and-tasks.org --week 2026-01-19 --dir out --format both
writing-schedule sheets projects-and-tasks.org --week 2026-01-19 --dir out --engine latex
Library
from writing_schedule import parse_text, generate_schedule, build_ics, render_week_pdf
parsed = parse_text(open("projects-and-tasks.org").read())
monday, title, body = generate_schedule(parsed, "2026-01-19")
open("writing-2026-01-19.org", "w").write(body)
open("writing-2026-01-19.ics", "w", newline="").write(build_ics(parsed, monday, title))
render_week_pdf(parsed, monday, "sheets-week-2026-01-19.pdf")
Module layout
| Module | Responsibility | Mirrors |
|---|---|---|
parser.py |
Read the org table into events | writing-schedule--parse |
orgtable.py |
Split org-table text into rows | org-table-to-lisp |
model.py |
Event, ParsedTable, MapEntry | the reference plists |
week.py |
Week arithmetic and date stamping | --week-monday, --iso-date |
schedule.py |
Write the dated schedule org file | writing-schedule-generate |
ics.py |
Emit RFC 5545 events directly | the ox-icalendar export |
vtimezone.py |
Build a VTIMEZONE from zoneinfo |
(improvement over floating time) |
sheet.py |
Draw the time-block PDF (ReportLab) | the --timeblock-* family |
sheet_latex.py |
Emit the LaTeX sheet (optional engine) | --timeblock-document |
sheet_org.py |
Write the editable week org file | --timeblock-org-document |
template.py |
Blank weekly-table scaffold | --template-string |
archive.py |
List archived weeks, newest first | --archived-weeks |
cli.py |
Subcommands and options | writing-schedule.sh |
The parser is free of any knowledge of output formats. It returns a plain data structure of events, the legend, the codes, and the day columns, so a bug in the PDF layout can never corrupt the calendar output.
The PDF decision
ReportLab is the default, because a person who cannot install Emacs is unlikely to enjoy installing a multi-gigabyte TeX distribution. Drawing the grid is a good fit for a canvas, because each planned block is a box placed at its exact time range rather than snapped to the five-row-per-hour grid the tabular model forces. The five sub-rows survive only as light guide lines. A block that reaches a page boundary is left open at that edge, so a block crossing the page break reads as one block across two pages.
The LaTeX emitter is kept behind --engine latex for users who have TeX and
want output that is byte-comparable with the Emacs version.
Daylight saving
Times are written as local wall-clock values with a TZID, and the file
carries a VTIMEZONE derived from zoneinfo. A 04:00 block therefore stays
at 04:00 in every week, and a calendar client resolves the correct absolute
instant on either side of a transition. The test suite verifies that a 04:00
block resolves to 10:00 UTC in a winter week and 09:00 UTC in a summer week.
Testing and parity
pip install -e ".[test]"
pytest
tests/test_writing_schedule.py ports the ERT suite to pytest and keeps the
same test names, so the two suites read side by side. tests/test_parity.py
asserts that the parser finds forty-four events with the correct codes,
offsets, and times in tests/fixtures/projects-and-tasks.org, which is the
hardest shared contract. tests/test_ics.py covers the calendar subset and
daylight saving. tests/test_sheet.py checks the PDF page count and the
block-box geometry, because byte comparison across two rendering engines is
not realistic.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file writing_schedule-0.1.0.tar.gz.
File metadata
- Download URL: writing_schedule-0.1.0.tar.gz
- Upload date:
- Size: 32.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46c14dca53e98202f5091779919b3e144766920598748d760cb39311e943e55c
|
|
| MD5 |
1892a30748ffb33cd2505d2fd8670e5c
|
|
| BLAKE2b-256 |
248eaefb6593438f669f6b4c5f52c8e83d0474b33196d8a8e60e0e5ec112466c
|
File details
Details for the file writing_schedule-0.1.0-py3-none-any.whl.
File metadata
- Download URL: writing_schedule-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ca0b7248990d852d3101e2ca0337e0cd0bc27cdef621c8d7451f8e1be07d4e3
|
|
| MD5 |
ab00ff54dbab768c1d36e549bb9c729d
|
|
| BLAKE2b-256 |
e2fb383b82352f135837567de2dfdaa55ef59d94ab678e2137d53decf6f56a52
|