Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Gather

Open-source scheduling for people and teams. A Python/Django alternative to hosted appointment tools, packaged as an Adios template. MIT licensed; no SaaS subscription, external scheduling service, or JavaScript build is required.

What you can do

  • Share a public workspace page and individual booking links.
  • Offer one-on-one, round-robin, or collective meetings with multiple hosts.
  • Set weekly hours, meeting lengths, buffers, minimum notice, and booking windows.
  • Book across IANA time zones and daylight saving changes.
  • Read busy times and write bookings to Google Calendar, Microsoft Outlook, and Apple iCloud Calendar. Each user can connect one calendar per provider.
  • Cancel or reschedule through a private confirmation link. A failed reschedule preserves the original reservation.
  • Invite teammates through private links that expire after seven days.
  • Download .ics events, optionally email confirmations, and retry failed calendar updates automatically.

The host UI and public booking pages are server-rendered Django templates with local CSS and a small progressive enhancement script. Booking works without JavaScript. Pipenv manages dependencies through Pipfile and Pipfile.lock, which records exact versions and package hashes, including transitive dependencies. The runtime is Python 3.13 with Django 5.2 LTS.

Run locally

Install Python 3.13 and Pipenv 2026.8.0 (for example, pipx install pipenv==2026.8.0), then:

export PIPENV_VENV_IN_PROJECT=1
export PIPENV_DONT_LOAD_ENV=1
pipenv verify
pipenv sync --python 3.13
export DJANGO_DEBUG=true
export PUBLIC_BASE_URL=http://localhost:8080
pipenv run python manage.py migrate
pipenv run python manage.py bootstrap --username alex --email alex@example.com \
  --team "Alex's workspace" --slug alex --timezone Europe/Paris
pipenv run python manage.py runserver 127.0.0.1:8080

The bootstrap command prompts for a password and creates a workspace owner, weekday 09:00–17:00 availability, and three editable booking links. There are no default accounts or passwords. For automated installation, supply the password through a temporary GATHER_ADMIN_PASSWORD environment variable and unset it after bootstrap. The command refuses to overwrite an existing workspace or user.

Open /login/ to manage the workspace and /alex/ to try the booking flow. The default meeting notice is two hours. SQLite is available only for local development and basic tests; use PostgreSQL to verify concurrent reservations. Environment files are examples only: export variables explicitly. Keep PIPENV_DONT_LOAD_ENV=1 set to prevent Pipenv from loading a local .env file.

runserver does not start the retry worker. In another terminal with the same environment, run pipenv run python manage.py sync_calendars --loop. The production command python -m scheduler.serve supervises both Gunicorn and this worker.

Deploy to Adios

Copy this directory into your own project and run:

adios up

The manifest follows the Django template contract: Python runtime, managed PostgreSQL 16, generated secrets, lockfile verification with python -m pipenv verify, locked dependency installation with python -m pipenv install --system --deploy, and collectstatic during build, a separate migrate --noinput release command, and a read-only database health probe at /healthz. Deployment identity is supplied by Adios. Adios installs dependencies into the runtime's Python environment, so its release, start, and terminal commands use python directly. The build fails if Pipfile.lock is missing or out of date.

After deployment:

  1. Set PUBLIC_BASE_URL to the exact public HTTPS origin, such as https://meet.example.com, with no path. This supplies absolute invitation, email, and OAuth callback URLs. The service can start without it so the initial deployment can complete; OAuth becomes available after configuration.
  2. Run python manage.py bootstrap with the arguments above in the app's terminal, with your real owner email, workspace name, slug, and time zone. The password is chosen interactively.
  3. Sign in, adjust the initial hours and booking links, connect calendars, and share your public workspace page.
  4. Configure SMTP if you want email confirmations. The confirmation page and calendar download remain usable without email.

Keep DJANGO_DEBUG disabled in production. The application requires PostgreSQL, DJANGO_SECRET_KEY, and CALENDAR_ENCRYPTION_KEY; Adios supplies these using the manifest. Back up PostgreSQL and retain the encryption key across deployments. Changing that key without re-encrypting existing credentials requires every calendar to be reconnected. For multiple replicas, all must share the same PostgreSQL database, origin, and keys.

Calendar setup

Google Calendar

Create a Google Cloud OAuth client of type Web application, enable the Google Calendar API, and configure its consent screen for your intended users. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in the deployment; store the secret in Adios secrets and reference it through secret://GOOGLE_CLIENT_SECRET.

Register this exact authorized redirect URI:

https://meet.example.com/calendars/google/callback/

The app requests offline access with PKCE and session-bound state. It uses the calendar.events, calendar.events.freebusy, and calendar.calendarlist.readonly scopes. In the host UI, connect Google and select a writable calendar. Google consent-screen testing restrictions and any verification requirements depend on your app's audience; configure the OAuth application for your actual deployment.

References: OAuth web-server flow, free/busy API, event creation.

Microsoft Outlook / Microsoft 365

Register a Microsoft Entra web application. To support both work/school accounts and Outlook.com, choose the account type allowing organizational directories and personal Microsoft accounts. Set MICROSOFT_CLIENT_ID and MICROSOFT_CLIENT_SECRET, using Adios secrets for the client secret.

Register this exact web redirect URI:

https://meet.example.com/calendars/microsoft/callback/

Grant delegated Calendars.ReadWrite access. Gather requests offline_access, uses the common OAuth endpoint with PKCE, and refreshes credentials server-side. An organization's administrator may need to approve access under its tenant policy. Connect Microsoft in the host UI and select a writable calendar.

References: Microsoft OAuth authorization-code flow, calendarView, event creation.

Apple Calendar / iCloud

Enable two-factor authentication on your Apple Account and generate an app-specific password under Sign-In and Security → App-Specific Passwords. In Gather, select Calendars → Connect Apple, enter your Apple Account email and that generated password, then choose an iCloud calendar.

The integration uses CalDAV at https://caldav.icloud.com and discovered iCloud calendar endpoints. It does not use Sign in with Apple or request your primary Apple Account password. Credentials are encrypted at rest. Calendars kept only under “On My Mac” or “On My iPhone” must first be moved to iCloud. This adapter targets iCloud; it is not a generic arbitrary-server CalDAV connector.

References: Apple app-specific passwords, Python CalDAV library.

Configuration

Variable Purpose
PUBLIC_BASE_URL Exact public HTTPS origin; HTTP is allowed only in development/tests.
DATABASE_URL PostgreSQL URL. URL-encoded credentials and query options such as sslmode are supported.
DJANGO_SECRET_KEY Stable signing secret, generated by Adios.
CALENDAR_ENCRYPTION_KEY Independent stable secret for authenticated credential encryption, generated by Adios.
DJANGO_ALLOWED_HOSTS Optional comma-separated hosts. Defaults to * for Adios internal health probes; absolute links use the configured origin.
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET Google OAuth application credentials.
MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET Microsoft OAuth application credentials.
SEND_BOOKING_EMAIL true enables confirmation/cancellation emails; default false.
EMAIL_HOST, EMAIL_PORT SMTP server and port; default port 587.
EMAIL_HOST_USER, EMAIL_HOST_PASSWORD SMTP authentication. Store the password as an Adios secret.
EMAIL_USE_TLS STARTTLS; default true.
DEFAULT_FROM_EMAIL Verified sender address for your SMTP provider.
GUNICORN_WORKERS, GUNICORN_THREADS Defaults: two workers, four threads each.

Adios terminates HTTPS. Only expose the application through your trusted ingress, which must overwrite X-Forwarded-Proto. Sessions and CSRF cookies are secure in production. A database-backed limiter covers login, booking, and connection attempts. It uses the direct peer address rather than trusting forwarded IP headers; configure visitor-aware limits at your trusted ingress for larger sites.

Scheduling and delivery behavior

  • PostgreSQL profile-row locks serialize booking attempts for each host across event types and workspaces. Collective meetings reserve all hosts. Round robin assigns an available host with the fewest confirmed bookings for that event.
  • Times are offered in 15-minute increments within each visitor's day. Hours repeat in the host's time zone; nonexistent DST times are not offered and repeated times retain distinct offsets. Hours are one same-day interval per weekday. Block holidays or one-off absences in a connected calendar.
  • Every selected provider calendar is checked live before a reservation. A provider outage stops booking rather than treating unknown availability as free. Calendar IDs are saved with each delivery, so changing the selected calendar does not move old meetings.
  • Reservations remain confirmed if a provider write fails. The dashboard and confirmation page show a pending update. A supervised worker retries calendar writes every minute; stable Google event IDs, Microsoft extended properties plus transaction IDs, and deterministic iCloud resource URLs prevent duplicate writes after a lost response. Manual retry: python manage.py sync_calendars in the deployed app's terminal, or pipenv run python manage.py sync_calendars locally.
  • Cancellation releases the local reservation and queues external cancellation. Until the external update succeeds, its busy event can still block that time. Rescheduling creates a new booking and cancels the old one atomically.
  • Provider events are host appointments. Gather sends its own optional guest emails and offers .ics downloads; it does not send provider attendee invites. Email failures are shown in the confirmation page and logs. Failed or rejected email sends are not automatically retried, to avoid repeated mail. A process crash after SMTP accepts a message but before its database commit can still produce a duplicate email; SMTP cannot guarantee exactly-once delivery.
  • Private booking and invitation URLs are bearer links. Anyone holding a booking link can view its guest details and cancel/reschedule it. Keep these URLs out of analytics and public sharing. Pages use no-store and a same-origin referrer policy, which suppresses these URLs on cross-origin navigation while preserving same-origin CSRF verification.

External calendar edits are not imported into Gather booking records: moving or deleting a provider event does not reschedule/cancel its local booking. Perform those actions through Gather. Provider busy reads and writes cannot be one atomic operation, so an event created directly in another calendar client after the final availability check can still race a booking. This initial template does not include video-conference provisioning, payments, reminders, webhook subscriptions, or an embedded iframe widget. Account recovery is available to the operator through python manage.py changepassword USERNAME in the deployed app's terminal (prefix with pipenv run locally).

Validation and contribution

pipenv verify
pipenv sync --dev
pipenv run python -m pip check
pipenv run python manage.py test scheduler
pipenv run python manage.py check
pipenv run python manage.py makemigrations --check --dry-run
pipenv run python manage.py collectstatic --noinput

For production locking tests, point DATABASE_URL at a disposable PostgreSQL instance with permission to create a test database, then run the same test command. SQLite skips the PostgreSQL concurrency test. Provider tests use fake HTTP and CalDAV responses; live OAuth consent and calendar writes require your own configured provider accounts. Never put real credentials or guest data in fixtures or exported template repositories.

The included GitHub Actions workflow installs with the same Pipenv deployment command as Adios and runs the suite in Python 3.13 against an isolated PostgreSQL 16 service, including the concurrency test.

To change dependencies, edit the pins in Pipfile, run pipenv lock, then pipenv sync --dev and the checks above. Commit both Pipfile and Pipfile.lock. CI and Adios consume the committed lockfile without updating it. See the Pipenv command reference for the dependency-management commands.

Source lives in config/ and scheduler/. Add migrations for model changes, preserve the host and event lock order for booking mutations, and keep tokens out of templates, logs, and Django admin. This directory is the canonical source for template-python-gather in the parent publication map. GitHub publication uses the repository's existing staging/digest gate.

License

MIT. You can use, customize, deploy, and redistribute Gather under the terms of that license.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages