Metadata-Version: 2.4
Name: plan-my-pycon
Version: 0.1.0
Summary: Interactive planner for PyCon AU 2026: pick between parallel talks and export ICS/JSON/Markdown/HTML.
Keywords: pycon,pycon-au,conference,calendar,ics,schedule,planner
Author: Mitchell Tesch
Author-email: Mitchell Tesch <mitchell.z.tesch@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business :: Scheduling
Classifier: Topic :: Utilities
Requires-Dist: beautifulsoup4>=4.12
Requires-Dist: requests>=2.31
Requires-Dist: rich>=13.7
Requires-Dist: questionary>=2.0
Requires-Dist: textual>=0.60
Requires-Python: >=3.11
Project-URL: Homepage, https://github.com/mitchell-tesch/plan-my-pycon
Project-URL: Repository, https://github.com/mitchell-tesch/plan-my-pycon
Project-URL: Issues, https://github.com/mitchell-tesch/plan-my-pycon/issues
Project-URL: Changelog, https://github.com/mitchell-tesch/plan-my-pycon/blob/main/CHANGELOG.md
Description-Content-Type: text/markdown

# plan-my-pycon

[![PyPI](https://img.shields.io/pypi/v/plan-my-pycon.svg)](https://pypi.org/project/plan-my-pycon/)
[![Python](https://img.shields.io/pypi/pyversions/plan-my-pycon.svg)](https://pypi.org/project/plan-my-pycon/)
[![CI](https://github.com/mitchell-tesch/plan-my-pycon/actions/workflows/ci.yml/badge.svg)](https://github.com/mitchell-tesch/plan-my-pycon/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

An interactive planner for **PyCon AU 2026** (Brisbane, 26–30 August 2026). It
scrapes the official schedule at <https://2026.pycon.org.au/schedule/>, fetches
every session's abstract and presenter bio, walks you through each
parallel-talk slot with a checkbox picker, and exports your picks as an ICS
(Google Calendar), JSON, Markdown or HTML file.

Supports all five days:

- **Wednesday** — pre-conference workshops
- **Thursday / Friday / Saturday** — main conference (default)
- **Sunday** — development sprints

## Install

Requires Python **3.11+**. Pick whichever tool you already have:

### With `uvx` (recommended — one-shot, no install)

```bash
uvx plan-my-pycon
```

### With `pipx` (installed globally, isolated)

```bash
pipx install plan-my-pycon
plan-my-pycon --version
```

Later:

```bash
pipx upgrade plan-my-pycon
```

### With `pip`

```bash
python -m pip install --user plan-my-pycon
```

### From source (for development)

```bash
git clone https://github.com/mitchell-tesch/plan-my-pycon.git
cd plan-my-pycon
uv sync
uv run plan-my-pycon
```

## Usage

Run the interactive planner (defaults to Thursday, Friday, Saturday):

```bash
plan-my-pycon           # slot-by-slot picker
plan-my-pycon --tui     # full-screen Textual TUI (all days at once)
```

For each timeslot with parallel talks you'll see a checkbox picker with a
progress hint (`Slot 4/12`) and per-track colour coding. Arrow keys navigate,
**space** toggles, **enter** confirms. Any items you selected on a previous
run — or that match filter flags — are pre-checked. Plenary sessions and
breaks are included automatically.

### Full-screen TUI (`--tui`)

`--tui` launches a full-screen [Textual](https://textual.textualize.io/)
app with:

- One tab per day, showing every timeslot and every parallel talk as a
  checkbox tinted by track colour.
- **Ctrl+F** to focus the filter box; type to highlight matching talks
  (title, presenter, track, and abstract text are searched).
- **Ctrl+A** to check every currently-highlighted talk in one go.
- **S** to save & exit, **Q** to quit without saving.
- Hover a talk to see its abstract as a tooltip.

### Review mode (`--review`)

The tool saves a fingerprint of the schedule alongside your picks. Re-run
with `--review` after the organisers update the schedule and only the slots
whose contents changed are shown — everything else is left untouched.

```bash
plan-my-pycon --review
```

Whenever a change is detected (even without `--review`) you'll see a red
panel summarising what moved:

```
! Schedule changed since last run
  Thursday 10:45  (+3 new, -1 removed)
  Friday   14:40  (2 edited)
```

### Configuration file

Pin your preferred defaults so you don't have to retype flags. Any of these
locations is picked up automatically (first match wins):

1. `--config PATH` on the CLI
2. `./plan-my-pycon.toml`
3. `[tool.plan-my-pycon]` in `./pyproject.toml`
4. `~/.config/plan-my-pycon/config.toml`

Or set `PLAN_MY_PYCON_CONFIG=/path/to/config.toml`.

Example `plan-my-pycon.toml`:

```toml
days = ["thursday", "friday", "saturday"]
format = "ics"
output = "pycon.ics"
selection = "pycon-picks.json"
reminders = ["10m", "1h"]
tui = true

[filters]
tracks = ["Data & AI"]
exclude_tracks = ["Platform Engineering"]
presenters = ["Fong-Jones"]
search = ["RAG|LLM"]

# All colours are hex strings. Any field you omit keeps its dark default.
[theme]
background = "#1A1B26"     # screen background
surface = "#24283B"        # timeslot cards
surface_alt = "#1E202E"    # filter bar
text = "#C0CAF5"           # primary text
text_muted = "#6B7280"     # meta text (room, presenter)
violet = "#9D7CFF"         # primary accent — parallel slots, focused input
emerald = "#34D399"        # secondary accent — plenary borders, input outline
coral = "#F87171"          # break labels
gold = "#FBBF24"           # highlight
match_bg = "#1F3B34"       # background for checkboxes matching the filter
header_bg = "#3B2E85"      # dark violet header bar
header_text = "#F5F1E8"    # header/footer text
footer_bg = "#0F1017"      # bottom bar
```

Explicit CLI flags always override the config file.

### TUI theme

The full-screen [`--tui`](#full-screen-tui---tui) uses a dark palette by
default, chosen so it contrasts well on any terminal and doesn't clash with
the site-palette **track colours** (Data & AI gold, Platform Engineering
coral, and so on). Every colour is themable via the `[theme]` table above —
tweak any field, leave the rest for the defaults. Text/background pairs are
checked for WCAG AA contrast (≥ 4.5:1 for body text, ≥ 3:1 for track labels)
in the test suite.

If you like a light background, set `background = "#F5F1E8"`, `surface =
"#ffffff"`, `text = "#282828"` (etc.) and you're back to the original site
palette.

### Import into Google Calendar

1. Open Google Calendar in a browser.
2. Settings > **Import & export** > **Import**.
3. Select `pycon-au-2026.ics` and choose the destination calendar.
4. Click **Import**.

All events use the `Australia/Brisbane` timezone (AEST, UTC+10, no DST).

### Filters — pre-select matching talks

Pre-check anything you already know you want by combining any of:

```bash
plan-my-pycon --track "Data & AI"                # only that track
plan-my-pycon --exclude-track "Platform Engineering"
plan-my-pycon --presenter "Fong-Jones|Ryan"      # regex over names
plan-my-pycon --search "RAG|LLM|OpenTelemetry"   # title/presenter/track/abstract
```

Flags are repeatable and are AND-combined. Matching talks are pre-checked in
the interactive picker; combine with `--non-interactive` for a hands-off run.

### Export formats

```bash
plan-my-pycon -f ics       # default — Google/Apple/Outlook calendars
plan-my-pycon -f json      # machine-readable
plan-my-pycon -f markdown  # paste into Notion, GitHub, Obsidian
plan-my-pycon -f html      # standalone printable page
```

### Other useful flags

```bash
plan-my-pycon --list                     # pretty-print the whole schedule
plan-my-pycon --days wednesday sunday    # plan workshops or sprints
plan-my-pycon --tui                      # full-screen TUI
plan-my-pycon --edit                     # re-open the picker even if saved picks exist
plan-my-pycon --review                   # only revisit changed slots
plan-my-pycon --non-interactive          # rebuild export from saved picks
plan-my-pycon --refresh                  # conditional re-fetch (If-Modified-Since)
plan-my-pycon --offline                  # network-free run from cache
plan-my-pycon --reminder 10m             # add VALARM alarms (repeatable)
plan-my-pycon --no-abstracts             # skip detail-page fetches (faster)
plan-my-pycon --no-color                 # plain output
plan-my-pycon --config my.toml           # explicit config file
plan-my-pycon --version                  # show version
plan-my-pycon -o me.md -s me.json        # custom output paths
```

### Smart re-runs

If you've already saved a selection and the schedule hasn't changed, subsequent
runs skip the picker and just re-export. Handy for "give me a fresh ICS after
tweaking `--reminder`":

```bash
plan-my-pycon --reminder 15m --reminder 1h   # re-export with new alarms
plan-my-pycon --edit                         # explicitly re-open the picker
plan-my-pycon --review                       # only revisit slots the organisers changed
```

### Conflict detection

After you save your selection the tool prints a red table of any overlapping
choices, so you can spot slots where you've kept two talks as backups. The
overlapping events remain in the export — a common workflow is to keep 2–3
options per slot and choose on the day.

### Reminders (VALARM)

Add ICS alarms so your calendar app pings you before every talk:

```bash
plan-my-pycon --reminder 10m                 # 10 minutes before
plan-my-pycon --reminder 5m --reminder 1h    # two reminders per event
plan-my-pycon --reminder 1h30m               # 1 hour 30 minutes
```

Durations accept any combination of `<int>h`, `<int>m`, and `<int>s`. A bare
integer (`--reminder 5`) is treated as minutes. Reminders are skipped on
break events. Set them permanently in `plan-my-pycon.toml`:

```toml
reminders = ["10m", "1h"]
```

### Offline mode

Fetched pages live under `~/.cache/plan-my-pycon/` (override with the
`PLAN_MY_PYCON_CACHE` env var). At the start of every run the tool prints the
age of each cached day (`Thursday schedule: cached (2h old)`) so you know
whether to `--refresh`.

- `--refresh` sends a conditional `If-Modified-Since` request; the server
  responds `304 Not Modified` when nothing has changed, so you only download
  what actually moved.
- `--offline` never touches the network. If a page isn't cached the tool
  exits cleanly with a message telling you to run without `--offline` once
  to populate the cache. Handy on planes, in trains, or on conference wifi.

```bash
plan-my-pycon --offline           # re-plan without network
plan-my-pycon --refresh           # re-validate cache, download only diffs
```

## Project layout

- `src/plan_my_pycon/scraper.py` — day + session-detail scraping, disk cache,
  retrying HTTP session.
- `src/plan_my_pycon/filters.py` — track / presenter / search filters.
- `src/plan_my_pycon/planner.py` — Rich + questionary interactive picker.
- `src/plan_my_pycon/tui.py` — Textual full-screen TUI (`--tui`).
- `src/plan_my_pycon/snapshot.py` — schedule fingerprinting for `--review`.
- `src/plan_my_pycon/conflicts.py` — overlap detection.
- `src/plan_my_pycon/exporters.py` — ICS / JSON / Markdown / HTML export.
- `src/plan_my_pycon/ics.py` — RFC 5545 ICS with Brisbane timezone.
- `src/plan_my_pycon/config.py` — TOML config loading.
- `src/plan_my_pycon/__init__.py` — CLI entrypoint.

## Releasing (maintainers)

### Local quality checks

Before pushing a change, run the same checks CI runs:

```bash
uv sync --group dev
uv run ruff format --check src tests
uv run ruff check src tests
uv run mypy
uv run pytest
uv build
```

Or all-in-one:

```bash
uv sync --group dev && uv run ruff check src tests && uv run mypy && uv run pytest && uv build
```

CI (`.github/workflows/ci.yml`) runs the same on every push/PR to `main`,
with pytest across Python 3.11, 3.12, and 3.13.

### Test fixtures

Tests never hit the network. Sample HTML for two conference days
(`day-thursday.html`, `day-sunday.html`) and three session detail pages live
under [`tests/fixtures/`](tests/fixtures) and are copied into a temp cache
directory by the `fixture_cache` fixture in
[`tests/conftest.py`](tests/conftest.py). To refresh them:

```bash
plan-my-pycon --refresh --days thursday sunday --list
cp ~/.cache/plan-my-pycon/day-{thursday,sunday}.html tests/fixtures/
cp ~/.cache/plan-my-pycon/session-{NDWRBS,3FQZVE,VHXDSA}.html tests/fixtures/
```

### Cutting a release

1. Bump `version` in `pyproject.toml` and add an entry to
   [`CHANGELOG.md`](CHANGELOG.md).
2. Build the wheel and sdist:

   ```bash
   uv build
   ```

3. Upload to PyPI (you'll need an API token — see
   <https://pypi.org/manage/account/token/>):

   ```bash
   uv publish
   # or: python -m twine upload dist/*
   ```

4. Tag the release:

   ```bash
   git tag v0.1.0 -m "v0.1.0"
   git push --tags
   ```

## License

MIT — see [`LICENSE`](LICENSE).

## Changelog

See [`CHANGELOG.md`](CHANGELOG.md).

