Metadata-Version: 2.4
Name: stapel-calendar
Version: 0.5.0
Summary: Calendar and scheduling for the Stapel framework
License: MIT
Project-URL: Homepage, https://github.com/usestapel/stapel-calendar
Project-URL: Repository, https://github.com/usestapel/stapel-calendar
Project-URL: Documentation, https://github.com/usestapel/stapel-calendar#readme
Project-URL: Changelog, https://github.com/usestapel/stapel-calendar/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/usestapel/stapel-calendar/issues
Keywords: django,stapel,calendar
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
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: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: stapel-core<1.0,>=0.27.0
Requires-Dist: python-dateutil<3,>=2.8
Provides-Extra: all
Dynamic: license-file

<!-- Generated by stapel-readme from docs/readme.md + docs/*.json. Do not edit this file; edit docs/readme.md and re-run `make readme`. -->

# stapel-calendar

[![CI](https://img.shields.io/github/actions/workflow/status/usestapel/stapel-calendar/ci.yml?branch=main&logo=github&label=CI)](https://github.com/usestapel/stapel-calendar/actions/workflows/ci.yml?query=branch%3Amain)
[![coverage](https://img.shields.io/codecov/c/github/usestapel/stapel-calendar?branch=main&logo=codecov&label=coverage)](https://app.codecov.io/gh/usestapel/stapel-calendar)
[![pypi](https://img.shields.io/pypi/v/stapel-calendar?logo=pypi&logoColor=white&label=pypi)](https://pypi.org/project/stapel-calendar/)
[![downloads](https://static.pepy.tech/badge/stapel-calendar/month)](https://pepy.tech/project/stapel-calendar)
[![python](https://img.shields.io/pypi/pyversions/stapel-calendar?logo=python&logoColor=white)](https://pypi.org/project/stapel-calendar/)
[![license](https://img.shields.io/github/license/usestapel/stapel-calendar)](https://github.com/usestapel/stapel-calendar/blob/main/LICENSE)
[![llms.txt](https://img.shields.io/badge/llms.txt-blue)](https://github.com/usestapel/stapel-calendar/blob/main/docs/llms.txt)

> Calendar and scheduling: events with participants and RSVP, RFC 5545 recurring series (virtual expansion, on-demand materialization, per-instant cancellation and reschedule), availability windows with free/busy and bookable slot computation, ICS export, and event-driven reminders.

Part of the [Stapel framework](https://github.com/usestapel) — composable Django apps that deploy as a monolith or as microservices without changing module code.

## Install

```bash
pip install stapel-calendar
```

## At a glance

| Fact | Value |
|---|---|
| Version | `0.5.0` |
| Python | `>=3.11` (3.11, 3.12, 3.13, 3.14) |
| HTTP operations | 10 |
| Config axes | 1 |
| Usage surface | 31 |
| Extension points | 5 |
| Error codes | 49 |
| Fleet dependencies | [`stapel-auth`](https://github.com/usestapel/stapel-auth) (optional) · [`stapel-core`](https://github.com/usestapel/stapel-core) · [`stapel-notifications`](https://github.com/usestapel/stapel-notifications) (optional) |

## Documentation

[OpenAPI](https://github.com/usestapel/stapel-calendar/blob/main/docs/schema.json) · [capabilities.json](https://github.com/usestapel/stapel-calendar/blob/main/docs/capabilities.json) · [llms.txt (for agents)](https://github.com/usestapel/stapel-calendar/blob/main/docs/llms.txt)

## What this is

A generic calendar core — **Event / Participant / RSVP**, an RFC 5545
**recurrence engine** (RRULE via `python-dateutil`, virtual expansion +
on-demand materialization), **availability** (working windows, free/busy,
slots) and **ICS export**. Two flavors of one domain — *meetings* and
*bookings* — share this core; everything app-specific lives behind seams.

## Quick start

```python
INSTALLED_APPS = [
    # ...
    "stapel_calendar",
]

# urls.py
path("calendar/", include("stapel_calendar.urls"))
```

## Concepts

- **Event** — a scheduled thing (meeting/booking/appointment) with tz-aware
  `start`/`end`, an `owner` and an opaque `scope_key`. No FK to any host
  concept (org, room) — scoping and resources are seams.
- **Recurrence** — the series master stores a canonical RRULE. Occurrences
  are *virtual* until they gain their own state (an RSVP or a resource), at
  which point they are *materialized* (persisted) on demand.
- **Availability** — recurring working windows minus busy events give open
  booking slots.

```python
from stapel_calendar import services

series = services.create_event(
    owner=user, title="Standup",
    start=start, end=end,
    recurrence_type="weekdays",            # -> FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR
)
occurrences = services.expand_event(series, range_start, range_end)  # virtual
services.materialize(series, occurrence_start)  # persist one + emit hook
busy = services.free_busy(user, range_start, range_end)
slots = services.compute_slots(user, range_start, range_end, slot_minutes=30)
```

## Settings

All configuration lives in the `STAPEL_CALENDAR` namespace (dict setting,
flat setting, or env var — resolved lazily):

| Key | Default | Meaning |
|---|---|---|
| `SCOPE_PROVIDER` | `…scope.DefaultScopeProvider` | Resolve/filter the opaque `scope_key` |
| `REMINDER_POLICY` | `…reminders.DefaultReminderPolicy` | When/what to remind |
| `PRESETS` | `{}` | Custom recurrence presets (merged over built-ins) |
| `REMINDER_OFFSETS` | `[10]` | Minutes before start to remind |
| `REMINDER_SCAN_WINDOW_SECONDS` | `60` | Reminder cron granularity |
| `DEFAULT_EXPANSION_HORIZON_DAYS` | `90` | Default range end |
| `MAX_EXPANSION_OCCURRENCES` | `1000` | Expansion safety cap |
| `DEFAULT_SLOT_MINUTES` | `30` | Default slot length |

## comm surface

| Kind | Name | Contract |
|---|---|---|
| Emit | `calendar.occurrence.materialized` | An occurrence was persisted — subscribe to attach a resource |
| Emit | `calendar.event.reminder_due` | A reminder is due — deliver it |
| Function | `calendar.free_busy` | `{user_id, start, end, scope_key?}` -> `{busy: [...]}` |

## Extension points

See [MODULE.md](https://github.com/usestapel/stapel-calendar/blob/main/MODULE.md)
— the agent-facing map of every fork-free seam (the resource hook, reminder
policy, scope provider, recurrence presets, serializer seams, settings).

## Development

```bash
pip install -e . && pip install pytest pytest-django ruff
./setup-hooks.sh
pytest tests/
```

## License

MIT — see [LICENSE](https://github.com/usestapel/stapel-calendar/blob/main/LICENSE).

---

<sub>This page is assembled by `stapel-readme` from `docs/readme.md` plus the contract artifacts in `docs/`. Edit the prose in `docs/readme.md`; the badges, facts and links above and below it are generated — do not hand-edit `README.md`.</sub>
