Calendar and scheduling for the Stapel framework
Project description
stapel-calendar
Calendar, recurrence and scheduling for the Stapel framework — composable Django apps that deploy as a monolith or as microservices without changing module code.
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.
Install
pip install stapel-calendar
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, anownerand an opaquescope_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.
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 — the agent-facing map of every fork-free seam (the resource hook, reminder policy, scope provider, recurrence presets, serializer seams, settings).
Development
pip install -e . && pip install pytest pytest-django ruff
./setup-hooks.sh
pytest tests/
License
MIT
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 stapel_calendar-0.1.0.tar.gz.
File metadata
- Download URL: stapel_calendar-0.1.0.tar.gz
- Upload date:
- Size: 40.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4436b39b93495381f4b1b593718d99a5b29171e25f9b277cd81fe657b72590a7
|
|
| MD5 |
2714f8221252a4123484bd78183fd75c
|
|
| BLAKE2b-256 |
32f89e38c59a2dc83a21277a662ea102fa9829613da55aded35c2abd39a9b3ad
|
Provenance
The following attestation bundles were made for stapel_calendar-0.1.0.tar.gz:
Publisher:
publish.yml on usestapel/stapel-calendar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_calendar-0.1.0.tar.gz -
Subject digest:
4436b39b93495381f4b1b593718d99a5b29171e25f9b277cd81fe657b72590a7 - Sigstore transparency entry: 2072901969
- Sigstore integration time:
-
Permalink:
usestapel/stapel-calendar@9e8e74d0b4b0f61ae641f7c73046fbc9a0be862f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9e8e74d0b4b0f61ae641f7c73046fbc9a0be862f -
Trigger Event:
push
-
Statement type:
File details
Details for the file stapel_calendar-0.1.0-py3-none-any.whl.
File metadata
- Download URL: stapel_calendar-0.1.0-py3-none-any.whl
- Upload date:
- Size: 52.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0fe88f378b17bdf0890be1937f90b0c8f78a765bb7866092e13d7945983604d
|
|
| MD5 |
4e58f46c919e78e538b1efd74e93363e
|
|
| BLAKE2b-256 |
3bc248d3dea139b5411e954a329f92b21f4e30cebd0ab7f1740fc9bf0a7832e1
|
Provenance
The following attestation bundles were made for stapel_calendar-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on usestapel/stapel-calendar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_calendar-0.1.0-py3-none-any.whl -
Subject digest:
b0fe88f378b17bdf0890be1937f90b0c8f78a765bb7866092e13d7945983604d - Sigstore transparency entry: 2072901986
- Sigstore integration time:
-
Permalink:
usestapel/stapel-calendar@9e8e74d0b4b0f61ae641f7c73046fbc9a0be862f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9e8e74d0b4b0f61ae641f7c73046fbc9a0be862f -
Trigger Event:
push
-
Statement type: