Agenda (Backend)
De Agenda-backend beheert afspraken, deelnemers, herinneringen en iCalendar-synchronisatie binnen de Remedice-omgeving. De logica zit in de features.agenda Django-app.
Technisch Ontwerp
De module volgt een service-layer architectuur: de views blijven dun en delegeren naar services.py.
Aanmaken en bijwerken
Bij het aanmaken of bijwerken van een agendapunt bepaalt de service de deelnemers, plant de herinneringen in via Celery en stuurt optioneel een melding.
flowchart TD
A[POST/PATCH AgendaItem] --> B[services.create/update_agenda_item]
B --> C{participants_mode}
C -- everyone --> D[Alle actieve tenant-gebruikers]
C -- selection --> E[Unie van AgendaParticipant<br/>users + rollen]
D --> F[resolve_participants]
E --> F
F --> G[upsert_reminders per deelnemer]
G --> H[ClockedSchedule + PeriodicTask<br/>per herinnering]
F --> I{notify_participants?}
I -- Ja --> J[notifications.enqueue_notification]
H --> K[Celery: send_agenda_reminder op tijdstip T]
Wanneer alleen de starttijd wijzigt, herberekent _reschedule_reminders de send_at van bestaande, nog niet verzonden herinneringen; herinneringen waarvan het nieuwe tijdstip in het verleden ligt, worden verwijderd.
Zichtbaarheidslogica
get_visible_items bepaalt welke items een gebruiker mag zien:
- Gebruikers met
agenda.editzien alle items binnen de tenant. - Overige gebruikers zien alleen items die zij zelf aanmaakten, items met
participants_mode = everyone, of items waar zij als deelnemer (viauser) of via hun rol (viarole) aan gekoppeld zijn.
Datamodel (ERD)
De structuur ondersteunt flexibele deelnemer-selectie en geautomatiseerde herinneringen. De primary keys van de agenda-entiteiten zijn UUID's; verwijzingen naar accounts.User gebruiken de integer primary key van dat model.
erDiagram
User ||--o{ AgendaItem : "created_by"
AgendaItem ||--o{ AgendaParticipant : "heeft"
AgendaItem ||--o{ AgendaReminder : "heeft"
User ||--o{ AgendaParticipant : "is deelnemer"
User ||--o{ AgendaReminder : "ontvangt"
User ||--o| WebcalToken : "heeft"
AgendaItem {
uuid id PK
string title
text description
string location
datetime start "db_index"
datetime end "nullable"
bool all_day
string participants_mode "everyone | selection"
bigint created_by_id FK "nullable, SET_NULL"
datetime created_at
datetime updated_at
}
AgendaParticipant {
uuid id PK
uuid agenda_item_id FK
bigint user_id FK "nullable"
string role "nullable"
}
AgendaReminder {
uuid id PK
uuid agenda_item_id FK
bigint user_id FK
string reminder_offset "at_time..2w"
datetime send_at "db_index"
datetime sent_at "nullable"
string celery_task_name
}
WebcalToken {
uuid id PK
bigint user_id FK "OneToOne"
uuid token "unique, db_index"
datetime created_at
}
- AgendaParticipant: een
CheckConstraintdwingt af dat precies een vanuserofroleis gezet (user XOR role). Rollen zijn waarden alsapotheker,assistent,arts. - AgendaReminder:
UniqueConstraintop(agenda_item, user, reminder_offset). Eenpre_deletesignaal ruimt de gekoppeldeClockedScheduleenPeriodicTaskop.
API & Communicatie
De API bestaat uit losse APIView-klassen onder /api/agenda/, met tenant-isolatie via TenantEnforcedJWTAuthentication.
GET /api/agenda/items/: gepagineerde lijst van zichtbare items. Query:date_from,date_to(ISO 8601),page,page_size(10/50/100). Zonder datums een bereik van 60 dagen rond nu, hard begrensd op 90 dagen. Antwoord:{ results, page, page_size, total, total_pages }.POST /api/agenda/items/: nieuw item. Body bevat onder meertitle,start,end,all_day,participants_mode,roles,people(integer User-PK's),reminders(first/secondoffset),notify_participants.GET /api/agenda/items/{item_id}/: details van een item.PATCH /api/agenda/items/{item_id}/: item bijwerken.DELETE /api/agenda/items/{item_id}/: item verwijderen (204).GET /api/agenda/users/: actieve gebruikers binnen de tenant voor de deelnemer-kiezer.GET /api/agenda/birthdays/: aankomende verjaardagen (komende 4 weken), gefilterd opshare_birthday_with_teamenvisible_to_teamuit het profiel. De leeftijd wordt nooit meegestuurd.GET /api/agenda/webcal-token/enDELETE /api/agenda/webcal-token/: het persoonlijke iCal-token ophalen respectievelijk roteren (de oude link vervalt).GET /api/agenda/webcal/{schema}/{token}/: ongeauthenticeerde iCalendar-feed (text/calendar), beveiligd via hetWebcalToken. Levert de zichtbare items van de tokenhouder (30 dagen terug, 365 dagen vooruit), metETag-gebaseerde304 Not ModifiedenX-Robots-Tag: noindex, nofollow.
Foutafhandeling & Statuscodes
400 Bad Request: ongeldige datumnotatie,date_tovoordate_from, een bereik groter dan 90 dagen, of validatiefouten op het serializer-niveau (zoals een ontbrekendetitle/start).403 Forbidden:POSTzonderagenda.edit;PATCH/DELETEdoor een gebruiker die noch eigenaar is nochagenda.editheeft.404 Not Found: het item bestaat niet in de tenant of is niet zichtbaar voor de gebruiker. De webcal-feed geeft ook404bij een ongeldig token, ongeldige schema-naam of een inactieve gebruiker (om accountstatus niet te lekken).304 Not Modified: webcal-feed wanneer deIf-None-MatchETag overeenkomt.429 Too Many Requests: bij overschrijden van de throttles (agenda_writeop schrijfacties,webcal_feedop de feed).
Autorisatie & Beveiliging
- Tenant Isolatie: alle queries draaien binnen het schema van de actieve tenant. De webcal-feed schakelt expliciet naar het juiste schema via
schema_contexten valideert de schema-naam tegen de django-tenants conventie ([a-z][a-z0-9_]{0,62}) voordat er een query draait. - RBAC: lezen (
GET /items/,/users/,/birthdays/,/webcal-token/) staat open voor elke ingelogde gebruiker;agenda.editverbreedt de zichtbaarheid tot alle items.POSTvereistagenda.edit.PATCHenDELETEvereisenagenda.editof eigenaarschap van het item. - Privacy: in
everyone-modus wordt hetdescription-veld in de detailserializer weggelaten voor gebruikers die niet de aanmaker of een expliciet gekoppelde deelnemer zijn. Verjaardagen verschijnen alleen met expliciete profieltoestemming en zonder leeftijd. - Webcal-feed: ongeauthenticeerd maar token-gebaseerd; toegang vervalt zodra de gekoppelde gebruiker inactief of verwijderd is.
Bestandsstructuur & Verantwoordelijkheden
models.py:AgendaItem,AgendaParticipant(user XOR role),AgendaReminder,WebcalTokenmet hun constraints en indexen.services.py:get_visible_items,resolve_participants,create_agenda_item,update_agenda_item,upsert_reminders/_reschedule_reminders,get_upcoming_birthdays.views.py: deAPIView-klassen met permissie-checks per methode en de ongeauthenticeerdeAgendaWebcalView.serializers.py: validatie en JSON-transformatie, inclusief de description-privacy inAgendaItemDetailSerializer.tasks.py:send_agenda_reminder(verstuurt de melding),cleanup_old_agenda_items(verwijdert items ouder dan 16 weken) encleanup_fired_oneoff_tasks.urls.py: URL-routering voor de module.
Belangrijke bestanden
backend/features/agenda/models.pybackend/features/agenda/services.pybackend/features/agenda/views.py