Ga naar inhoud

Medicatiebeoordeling (Backend)

Technisch Ontwerp

De backend van de module Medicatiebeoordeling parseert AIS- en HIS-data, verrijkt iedere medicatie met G-Standaard-informatie en draait een vaste set klinische analyses per patiënt. De zware verwerking loopt asynchroon via Celery zodat de aanmaak-endpoint direct kan terugkeren.

  • AIS-parsers (parsers/ais/) zetten ruwe tekst of een PDF om naar ParsedReview(afdeling, patients). Iedere parser implementeert parse(text, geboortedatum_override, scope) waarin scope "afdeling" of "patient" is. Wanneer de parser een PDF accepteert is parse_file(file_bytes, ...) beschikbaar.
  • De HIS-parser voor Promedico levert contra-indicaties en labwaarden per patiënt.
  • ATC-, ICPC- en NHG-labwaarde-koppelingen verlopen uitsluitend via lookup.py en de gedeelde SQLite-database data/lookup.db (pad uit settings.LOOKUP_DB_PATH).
  • Jansen-groepering gebeurt in jansen.py op basis van de ATC3-code; de uitkomst wordt opgeslagen op ReviewMedicatie.jansen_group_id en jansen_group_naam.
  • Standaardvragen worden geseed vanuit data/standaardvragen/standaardvragen_base.json via het management-command seed_standaardvragen (gebruikt get_or_create; respecteert de DeletedBaseVraag-tombstones en is_customized-flag).
  • De zes klinische analyses zijn analyse_start_stop (STOPP-NL V2), analyse_acb, analyse_dubbelmedicatie, analyse_standaardvragen, analyse_bijwerkingen en analyse_maagbescherming. De resultaten worden opgeslagen in ReviewAnalyse.data (encrypted JSON), uniek per (patient, type).
  • Indicatie- en labwaarderegels op standaardvragen sluiten een vraag nooit uit. Ze komen ongewijzigd terug als extra_criteria in de analyse-output en worden door de frontend als aandachtspunten getoond.
  • Real-time samenwerking verloopt via Django Channels: PatientCollabConsumer broadcast patient_data_changed-events naar alle actieve dossier-sessies.
  • Patiëntgesprekken hebben een eigen pipeline (gesprek_service.py + tasks.py): opname-upload, transcriptie via AWS Transcribe (transcribe_audio_task) en samenvatting via een Bedrock-LLM-call (generate_summary_task); finalize_gesprek_task ketent beide na een mislukte live-stream. Statusvelden opname_status, transcriptie_status en samenvatting_status worden gepoll't door de frontend.
  • Exports (PDF/DOCX) lopen via ReviewExport-rijen die door een Celery-task worden gerenderd en in S3 opgeslagen; downloads gaan via een short-lived presigned URL.
  • Archivering: bij een vierde review op dezelfde patiënt of afdeling plant _maybe_archive_oldest_* via transaction.on_commit een Celery-task die de oudste review naar het archief verplaatst.
flowchart TD
    A[POST /reviews/] --> B[parse_review_input]
    B --> C{billing.check_review_gate}
    C -- ok --> D[start_review_async]
    C -- blocked --> X[402 BillingGateError]
    D --> E[MedicatieReview row\nprocessing_status=processing]
    E --> F[Celery: run_review_task]
    F --> G[services._populate_review]
    G --> H[lookup.lookup_atc / enrich_from_atc_code]
    G --> I[jansen.get_jansen_group]
    G --> J[HIS Promedico parser\n+ best_icpc_match / best_labwaarde_match]
flowchart TD
    G[services._populate_review] --> K[6x analyses]
    K --> K1[analyse_start_stop]
    K --> K2[analyse_acb]
    K --> K3[analyse_dubbelmedicatie]
    K --> K4[analyse_standaardvragen\nextra_criteria]
    K --> K5[analyse_bijwerkingen]
    K --> K6[analyse_maagbescherming]
    K --> L[ReviewAnalyse rows + opmerkingen]
    L --> M[processing_status=done\n+ processing_warnings]
    M --> N[Frontend poll GET /reviews/id/]
    G --> O[on_commit: archive_old_review_task\nbij 4e review]

Datamodel (ERD)

Het datamodel is opgesplitst in kleinere ERD's. Daardoor blijft de structuur leesbaar op desktop en mobiel: eerst de reviewkern, daarna medicatie, klinische gegevens, communicatie en audit.

Reviewkern

erDiagram
    MedicatieReview ||--o{ ReviewPatient : "patients"
    PatientProfiel ||--o{ ReviewPatient : "review_patients"

    MedicatieReview {
        uuid id PK
        uuid created_by FK
        string ais_source
        string his_source
        string afdeling
        string status
        string processing_status
        text processing_error
        json processing_warnings
        bool is_archiving
        datetime created_at
        datetime updated_at
    }
    ReviewPatient {
        uuid id PK
        uuid review_id FK
        uuid profiel_id FK
        string naam
        date geboortedatum
        smallint leeftijd
        string geslacht "m | v | onbekend"
        smallint position
        string status
        bool valrisico_actief
        text valrisico_toelichting
        bool malen_actief
        text malen_toelichting
        text algemeen_notities
        bool atc_modal_shown
    }
    PatientProfiel {
        uuid id PK
        uuid created_by FK
        string naam
        date geboortedatum
        datetime created_at
    }

Medicatie en groepering

erDiagram
    ReviewPatient ||--o{ ReviewMedicatie : "medicaties"
    ReviewPatient ||--o{ ReviewMedicatieGroep : "groepen"
    ReviewPatient ||--o{ ReviewPatientJansenGroep : "jansen_groepen"

    ReviewPatient {
        uuid id PK
        uuid review_id FK
        uuid profiel_id FK
        string naam
        string status
    }
    ReviewMedicatie {
        uuid id PK
        uuid patient_id FK
        string naam
        string naam_raw
        json metadata
        string atc_code
        string atc3
        string atc4
        string atc5
        string atc7
        string atc3_omschrijving
        string atc4_omschrijving
        string atc5_omschrijving
        string atc7_omschrijving
        smallint jansen_group_id
        string jansen_group_naam
        smallint position
    }
    ReviewMedicatieGroep {
        uuid id PK
        uuid patient_id FK
        string naam
        string kleur
        smallint position
    }
    ReviewPatientJansenGroep {
        uuid id PK
        uuid patient_id FK
        smallint jansen_group_id
        string jansen_group_naam
    }

Klinische gegevens en analyses

erDiagram
    ReviewPatient ||--o{ ReviewAnalyse : "analyses"
    ReviewPatient ||--o{ ReviewContraIndicatie : "contra_indicaties"
    ReviewPatient ||--o{ ReviewAllergie : "allergieen"
    ReviewPatient ||--o{ ReviewLabwaarde : "labwaarde_results"
    ReviewPatient ||--o{ ReviewPatientOpmerking : "opmerkingen_set"

    ReviewPatient {
        uuid id PK
        uuid review_id FK
        uuid profiel_id FK
        string naam
        string status
    }
    ReviewAnalyse {
        uuid id PK
        uuid patient_id FK
        string type
        json data
    }
    ReviewContraIndicatie {
        uuid id PK
        uuid patient_id FK
        string code
        string omschrijving
    }
    ReviewAllergie {
        uuid id PK
        uuid patient_id FK
        string atc_code
        string omschrijving
    }
    ReviewLabwaarde {
        uuid id PK
        uuid patient_id FK
        string code
        string omschrijving
        string waarde
        string eenheid
        date datum
    }
    ReviewPatientOpmerking {
        uuid id PK
        uuid patient_id FK
        string groep_naam
        text tekst
        int version
    }

Standaardvragen

erDiagram
    Standaardvraag ||--o| DeletedBaseVraag : "tombstone"

    Standaardvraag {
        uuid id PK
        string vraag_id UK
        string title
        text description
        string category
        json primary_triggers
        json logic_rules
        smallint age_min
        smallint position
        bool is_active
        bool is_base
        bool is_customized
    }
    DeletedBaseVraag {
        string vraag_id UK
    }

Gesprekken en Atlas-chat

erDiagram
    ReviewPatient ||--|| ReviewPatientGesprek : "gesprek"
    ReviewPatient ||--o{ ReviewChatThread : "chat_threads"
    ReviewChatThread ||--o{ ReviewChatMessage : "messages"
    ReviewPatientGesprek ||--o{ GesprekOpnameStart : "opname_starts"

    ReviewPatient {
        uuid id PK
        uuid review_id FK
        uuid profiel_id FK
        string naam
        string status
    }
    ReviewPatientGesprek {
        uuid id PK
        uuid patient_id FK
        text transcriptie
        text conclusie
        text ai_conclusie
        string ai_conclusie_model_id
        string opname_status
        string transcriptie_status
        string samenvatting_status
        string audio_filepath
    }
    GesprekOpnameStart {
        uuid id PK
        uuid gesprek_id FK
        datetime started_at
    }
    ReviewChatThread {
        uuid id PK
        uuid patient_id FK
        string title
        string pending_tool_name "HITL PubMed-pauze"
        json pending_tool_payload
        datetime pending_tool_created_at
        bool pubmed_approved
        datetime created_at
        datetime updated_at
    }
    ReviewChatMessage {
        uuid id PK
        uuid patient_id FK
        uuid thread_id FK
        string role
        text content "encrypted"
        json sources
        json citations
        text thinking_content "encrypted"
        smallint thinking_seconds
        string model_id
        string patient_pseudo_id "audit voor Atlas patientcontext"
        json patient_context_fields_sent "audit, alleen keys"
        string patient_context_source "medicatiebeoordeling"
        datetime created_at
    }

Exports, audit en uitnodigingen

erDiagram
    MedicatieReview ||--o{ ReviewAuditEntry : "audit_entries"
    MedicatieReview ||--o{ ReviewExport : "exports"
    MedicatieReview ||--o{ ReviewConsentAttestation : "consent_attestations"
    PatientProfiel ||--o{ PatientInvitation : "invitations"

    MedicatieReview {
        uuid id PK
        uuid created_by FK
        string afdeling
        string status
    }
    PatientProfiel {
        uuid id PK
        uuid created_by FK
        string naam
        date geboortedatum
    }
    ReviewExport {
        uuid id PK
        uuid review_id FK
        uuid patient_id FK
        string kind
        string filetype
        string status
        string s3_key
        datetime created_at
    }
    ReviewAuditEntry {
        uuid id PK
        uuid review_id FK
        string patient_id
        uuid user_id FK
        string action
        text description
        json metadata
        datetime created_at
    }
    ReviewConsentAttestation {
        uuid id PK
        uuid review_id FK
        uuid patient_id FK
        uuid gesprek_id FK
        string consent_type
        string scope
        string wording_version
        datetime accepted_at
    }
    PatientInvitation {
        uuid id PK
        uuid patient_profiel_id FK
        string patient_naam_snapshot "encrypted"
        date geboortedatum_snapshot "encrypted"
        string email "encrypted"
        string phone_e164 "encrypted"
        string token_hash UK
        string status
        json channels_requested
        json channels_sent
        json delivery_warnings
        string letter_status
        string letter_provider_ref
        string letter_pdf_s3_key
        uuid created_by FK
        datetime created_period_start
        datetime expires_at
    }

Postadres-velden voor het briefkanaal (street, house_number, postcode, city, country) staan eveneens op PatientInvitation en zijn versleuteld; ze zijn hierboven weggelaten om het diagram leesbaar te houden.

Kanaalbilling, opt-outs en patiëntrechten

erDiagram
    PatientInvitation ||--o{ ChannelBillingEvent : "channel_billing_events"
    PatientProfiel ||--o{ PatientInvitationOptOut : "invitation_optouts"
    PatientProfiel ||--o{ PatientErasureRequest : "erasure_requests"

    PatientInvitation {
        uuid id PK
        string status
    }
    PatientProfiel {
        uuid id PK
        string naam
        date geboortedatum
    }
    ChannelBillingEvent {
        uuid id PK
        uuid invitation_id FK
        string channel "letter | sms"
        datetime period_start
        int amount_cents_excl_vat
        string stripe_meter_event_id
        text stripe_sync_error
        datetime created_at
    }
    PeriodInviteUsage {
        datetime period_start PK
        int email_sent
        int sms_sent
        int letter_sent
        datetime updated_at
    }
    PatientInvitationOptOut {
        uuid id PK
        string channel "email | sms | letter"
        string contact_hash
        uuid patient_profiel_id FK
        bool active
        string actor_type "patient | staff"
        uuid set_by_id FK
        datetime created_at
    }
    PatientErasureRequest {
        uuid id PK
        uuid patient_profiel_id FK
        uuid patient_profiel_snapshot
        uuid requested_by_id FK
        text motivation "encrypted"
        string status "requested | executed | rejected"
        string execution_mode "pseudonymized | hard_deleted"
        datetime last_activity_at_decision
        text rejection_reason
        datetime executed_at
    }

PeriodInviteUsage is tenant-impliciet (telt per factuurperiode binnen het tenant-schema) en heeft daarom geen FK; period_start is de natuurlijke sleutel, gekopieerd van de public-schema BillingPeriod. ChannelBillingEvent gebruikt PROTECT op de invitation-FK zodat het financiële spoor intact blijft.

API & Communicatie

Alle endpoints zijn gemount onder /api/medicatiebeoordeling/ en vereisen JWT-authenticatie binnen de tenant, behalve de publieke patiëntpagina-endpoints (AllowAny, token-gebaseerd). De endpoints zijn hieronder gegroepeerd per scherm-gebied zodat de groepering de frontend volgt. De vereiste permissiecode staat per groep; zie ook Autorisatie & Beveiliging.

Reviews & overzicht (Overzicht-scherm)

  • GET /reviews/?afdeling=<naam>&q=<zoek> - lijst eigen reviews, optioneel gefilterd op afdeling; q activeert globale zoek. (review.view)
  • GET /reviews/<id>/ - review inclusief patiëntenlijst, processing_status en processing_warnings. (review.view)
  • PATCH /reviews/<id>/ - update bijvoorbeeld afdeling of status. (review.edit)
  • DELETE /reviews/<id>/ - verwijdert een eigen review (owner-scoped). (review.edit)
  • GET /ping/ - health check.

Nieuwe beoordeling

  • POST /reviews/ - start een nieuwe review. Body: ais_source, his_source, text of geüpload bestand, scope (afdeling/patient), optioneel patient_naam (verplicht voor Medimo patient-scope, niet toegestaan voor afdelingsscope), geboortedatum, patient_profiel_id, afdeling_naam. Antwoord: review-row met processing_status="processing"; de frontend pollt daarna GET /reviews/<id>/. (review.edit)

Patiëntdetail (Patiëntdossier)

  • GET /reviews/<id>/patients/<pid>/ - volledig dossier: medicatie (per Jansen-groep), analyses, contra-indicaties, allergieën, labwaarden en collab-state. (review.view)
  • PATCH /reviews/<id>/patients/<pid>/ - dossiermutaties. PATCH /patients/<pid>/status/ en PATCH /reviews/<id>/bulk_patient_status/ voor status (in_voorbereiding/voorbereid/uitgevoerd). (review.edit)
  • POST /reviews/<id>/patients/<pid>/dossier-opened/ - audit-event dat het dossier is geopend. (review.view)
  • Medicatie en groepen: PATCH .../medicaties/<mid>/, GET/POST .../groepen/, GET/PATCH .../groepen/<gid>/. (review.edit)
  • Notities en analyses: PATCH .../algemeen/, PATCH .../maagbescherming/, GET/POST .../opmerkingen/. (review.edit)
  • Klinische hints: GET/POST /patients/<pid>/contra-indicaties/ + DELETE /contra-indicaties/<id>/; idem allergieen/, labwaarden-results/, jansen-groepen/. Indicatie- en labwaarderegels sluiten nooit uit; ze komen als aandachtspunten terug. (review.edit)
  • GET /reviews/<id>/patients/<pid>/history/ - audit-timeline van het dossier. (review.view)
  • PATCH /reviews/<id>/patients/<pid>/profiel/ - koppel het patiëntdossier aan een bestaand PatientProfiel. (review.edit)

Afdeling

  • GET /afdelingen/, GET /afdelingen/<naam>/ - afdelingenlijst en afdelingsdetail (alle patiëntreviews). (review.view)
  • DELETE /afdelingen/<naam>/, POST /afdelingen/<naam>/export/<pdf|docx>/. (review.edit)

Historie

  • GET /reviews/ en GET /afdelingen/ met paginatie (page, page_size) voor de twee historie-tabellen; verwijderen via dezelfde review- en afdeling-DELETE's. (review.view / review.edit)

Gesprek & opname

  • GET/PATCH /reviews/<id>/patients/<pid>/gesprek/ - haalt of initialiseert het ReviewPatientGesprek; PATCH bewaart handmatig transcriptie/conclusie. (review.edit)
  • GET .../gesprek/status/ - pollt opname_status, transcriptie_status, samenvatting_status en fouten. (review.view)
  • POST .../gesprek/opname/upload/ - upload audio (start transcriptie); POST .../gesprek/finalize/ (samenvatting + consent + billing); POST .../gesprek/cancel/. (review.edit)
  • GET/PUT .../gesprek/notify-preference/ - notificatievoorkeur na verwerking. (review.edit)
  • POST .../gesprek/ws-ticket/ en POST .../collab-ticket/ - kortlevende tickets voor de audio-WebSocket respectievelijk de collab-WebSocket (PatientCollabConsumer). (review.edit)

Atlas-zijbalk (in dossier)

  • POST /reviews/<id>/patients/<pid>/atlas/stream/ - SSE-chat. Accepteert include_patient_context: bool; alleen wanneer ook de tenant-kill-switch (Apotheek.atlas_patient_context_enabled) aanstaat, prependt de server een gepseudonimiseerde patiëntcontext (allowlist via features/atlas/patient_context.build_patient_context) voor RAG-retrieval. Audit-velden worden op ReviewChatMessage vastgelegd. (review.edit)
  • POST .../atlas/pubmed-search/, POST .../atlas/tool-decline/ - HITL PubMed-tool goedkeuren/afwijzen. (review.edit)
  • GET/POST .../atlas/threads/, GET/PATCH .../atlas/threads/<tid>/, POST .../atlas/threads/<tid>/archive/. (review.edit)

Export

  • POST /reviews/<id>/patients/<pid>/export/<pdf|docx>/ en POST /afdelingen/<naam>/export/<pdf|docx>/ - maken een ReviewExport-job aan. GET /exports/<id>/ pollt de status en levert bij done een short-lived presigned S3-URL. (review.edit / review.view)

Standaardvragen & lookups (Instellingen-scherm)

  • GET /standaardvragen/, GET /standaardvragen/<vraag_id>/, PUT /standaardvragen/<vraag_id>/. Muteren vereist review.manage_settings.
  • GET /atc-lookup/?q=<query>&len=1|3|5,7 (G-Standaard), GET /icpc-lookup/?q=, GET /labwaarden-lookup/?q=, GET /maagbescherming-config/. (review.view/review.edit)

Uitnodigingen

  • GET/POST /invitations/ - lijst (cursor-paginatie, filter op status) en aanmaken (email/SMS/brief). (review.manage_invites)
  • POST /invitations/precheck/ - valideert contact + opt-out + bundel/cap vóór verzending. GET /invitations/usage/ - kanaalverbruik in de factuurperiode. (review.manage_invites)
  • POST /invitations/<id>/revoke/ (zet status="revoked"), POST /invitations/<id>/opt-out/ (staff-override per kanaal). (review.manage_invites)
  • Previews: GET /invitations/preview/email/, .../preview/sms/, .../preview/letter/ plus POST .../preview/letter/ticket/ en GET .../preview/letter/redeem/ voor een presigned brief-PDF. (review.manage_invites)
  • GET /channel-settings/, POST /channel-settings/<channel>/ - branding en sjablonen per kanaal. (settings.manage_billing)
  • Publieke patiëntpagina (token-gebaseerd, AllowAny): de patiënt opent een link met een ondoorzichtig token (token_hash) en kan de uitnodiging accepteren, weigeren of zich per kanaal afmelden (PatientInvitationOptOut, AVG art. 21). Deze endpoints raken nooit het dossier; ze zetten alleen de invitation-status en opt-out.

Patiëntrechten (AVG)

  • GET /patient-profielen/ (paginatie, q-zoek op naam, include_orphaned=1 voor dossiers zonder actieve review), POST /patient-profielen/fuzzy-match/, GET /patient-profielen/<id>/history/. (review.view)
  • GET /patient-profielen/<id>/dsar-export/ - export van alle patiëntdata (AVG art. 15). (review.dsar_export + step-up)
  • POST /patient-profielen/<id>/erase-request/ (motivering, WGBO-beoordeling) en POST /patient-profielen/<id>/erase-execute/ (Tier-3: confirmation="BEVESTIG" plus verse single-use TOTP- of biometrie-bewijs). Binnen de WGBO-bewaartermijn pseudonimiseren, daarna hard-delete; het S3-archief blijft onaangeroerd. (review.erase_patient)

WebSocket-events

Collab- en gesprek-kanalen volgen de envelope { "type", "status", "payload" }. Belangrijkste types: patient_data_changed (na een mutatie, met editor_name, editor_type, reason), field_text_changed (diff-sync per tekstveld met version), presence_join/presence_leave, en gesprek-events tijdens opname, transcriptie en finalize.

Foutafhandeling & Statuscodes

  • 400 Bad Request - validatiefouten, onleesbare PDF, ontbrekende verplichte velden of foutieve scope/patient_naam-combinatie. Voor Medimo afdelingsscope is patient_naam niet toegestaan.
  • 401 Unauthorized - JWT ontbreekt of is verlopen.
  • 402 Payment Required - BillingGateError: review-cap of trial uitgeput. De response bevat een typed error-code zodat de frontend een specifieke modal kan tonen.
  • 403 Forbidden - tenant-mismatch of ontbrekende permissie (review.view, review.edit, review.manage_settings).
  • 404 Not Found - review/patient/medicatie behoort niet tot de tenant of de gebruiker.
  • 409 Conflict - versie-conflict bij gelijktijdige edits op dezelfde tekstvelden (versie-veld op ReviewPatient en op opmerkingen).
  • 422 Unprocessable Entity - parser kon de AIS-tekst niet ontleden.
  • 429 Too Many Requests - ratelimit op chat- en uitnodigingsendpoints (ratelimit.py).
  • 500 Internal Server Error - onverwachte fout in lookup, analyse of Celery-task. De review-row krijgt processing_status="failed" met processing_error.

Autorisatie & Beveiliging

  • Tenant-isolatie via django-tenants en TenantEnforcedJWTAuthentication. Iedere query loopt binnen het schema van de huidige apotheek; cross-tenant FK's zijn niet toegestaan (vandaar de gekopieerde period_start op uitnodigingstellers in plaats van een FK naar de public-schema BillingPeriod).
  • RBAC via HasPermissionCode (gedeeld symbool PATIENT_DATA_PERMISSIONS in permissions.py). Codes uit de catalogus: review.view (bekijken), review.edit (uitvoeren), review.manage_settings (standaardvragen), review.manage_invites (patiënten uitnodigen), review.letter_admin (brief-/SMS-instellingen), review.dsar_export (AVG art. 15), review.erase_patient (AVG art. 17 / WGBO). Kanaal-branding en -verbruik vallen onder settings.manage_billing.
  • Step-up re-auth in drie tiers (common/stepup):
    • Tier-1 (module betreden) en Tier-2 (mutaties in het dossier) rijden op een 30-minuten verse-elevatie via PATIENT_DATA_PERMISSIONS_STEPUP. RBAC gaat voor: een niet-gerechtigde gebruiker krijgt een autorisatiefout, een gerechtigde-maar-niet-geëleveerde gebruiker krijgt step_up_required.
    • Tier-3 (erasure-uitvoering) houdt bewust de gewone permissieset en verifieert inline een verse, eenmalige TOTP- of biometrie-code; de gewone 30-minuten-elevatie volstaat hier niet.
  • Owner-scoping: reviews zijn altijd gefilterd op created_by=request.user. DELETE op een review werkt alleen voor de eigenaar.
  • Encryptie at rest: gevoelige velden (naam, geboortedatum, medicatie, ATC, labwaarden, transcriptie, conclusie, audit-beschrijvingen, uitnodigingscontact) gebruiken EncryptedCharField/EncryptedTextField/EncryptedDateField/EncryptedJSONField uit encryption.py (Fernet met sleutelrotatie).
  • Audit-log: iedere mutatie logt via log_review_action naar ReviewAuditEntry. Het review_uuid_snapshot-veld en de SET_NULL-FK houden de audit-trail intact als de review wordt verwijderd (NEN 7513 §6.2).
  • Gepseudonimiseerde Atlas-context: alleen de top-level keys, een pseudo-id en de bronmodule worden gelogd op ReviewChatMessage, nooit de inhoud van de context.
  • Consent: opnames en data-analyse vereisen een ReviewConsentAttestation met wording_version (scope patiënt of afdeling). Zonder geattesteerde toestemming worden opname- en samenvattings-endpoints geweigerd.
  • Patiëntportaal-uitnodigingen gebruiken een gehasht token (token_hash) en vervallen na expires_at; intrekking zet status="revoked". Opt-outs keyen op een gezouten SHA256-contact_hash zodat ze ook werken voor contact-only uitnodigingen zonder profiel.
  • Langetermijnbewaring: gearchiveerde reviews gaan naar S3 met 20-jaar Object Lock (WGBO 7:454 / NEN 7513); de S3-export is canoniek en blijft bij erasure onaangeroerd.
  • Ratelimiting op chat- en uitnodigingsendpoints via ratelimit.py.

Bestandsstructuur & Verantwoordelijkheden

  • backend/features/medicatiebeoordeling/models.py - Django-modellen voor reviews, patiënten, medicatie, analyses, opmerkingen, gesprekken, exports, audit, uitnodigingen en consent.
  • backend/features/medicatiebeoordeling/services.py - run_review, start_review_async, _populate_review, profiel-koppeling, archivering, audit-helpers, standaardvragen-seed.
  • backend/features/medicatiebeoordeling/parsers/registry.py - registry voor AIS-parsers.
  • backend/features/medicatiebeoordeling/parsers/ais/{medimo,pharmacom,sanday,cgm}.py - per-AIS implementaties van AISParser.
  • backend/features/medicatiebeoordeling/parsers/his/{registry.py,promedico.py} - HIS-parser voor Promedico.
  • backend/features/medicatiebeoordeling/lookup.py - sole interface naar data/lookup.db: lookup_atc, enrich_from_atc_code, lookup_atc_allergie, best_icpc_match, best_labwaarde_match, icpc_lookup, labwaarden_lookup.
  • backend/features/medicatiebeoordeling/jansen.py - Jansen-categorisatie op basis van ATC3.
  • backend/features/medicatiebeoordeling/analyses/start_stop.py - STOPP-NL V2.
  • backend/features/medicatiebeoordeling/analyses/acb.py - ACB-score.
  • backend/features/medicatiebeoordeling/analyses/dubbelmedicatie.py - dubbelmedicatie-detectie.
  • backend/features/medicatiebeoordeling/analyses/standaardvragen.py - standaardvraag-engine inclusief extra_criteria voor indicatie- en labwaardehints.
  • backend/features/medicatiebeoordeling/analyses/bijwerkingen.py - bijwerkingen-heatmap.
  • backend/features/medicatiebeoordeling/analyses/maagbescherming.py - maagbescherming-analyse met overrides en ICPC-context.
  • backend/features/medicatiebeoordeling/data/lookup.db - gedeelde SQLite voor G-Standaard en NHG-labwaarden.
  • backend/features/medicatiebeoordeling/data/acb-score/acb.json, data/stop-v2/stop-v2.json, data/standaardvragen/standaardvragen_base.json, data/jansen-groups/{jansen_groups,atc_to_jansen}.json, data/maagbescherming/ - referentiedata.
  • backend/features/medicatiebeoordeling/views.py - DRF-views voor alle REST-endpoints.
  • backend/features/medicatiebeoordeling/views_invitations.py - patiëntportaal-uitnodigingen.
  • backend/features/medicatiebeoordeling/serializers.py, serializers_invitations.py - DRF-serializers.
  • backend/features/medicatiebeoordeling/urls.py - routing.
  • backend/features/medicatiebeoordeling/consumers.py, collab.py - Channels-consumers en broadcast-helpers.
  • backend/features/medicatiebeoordeling/gesprek_service.py - opname-, transcriptie- en samenvattingspipeline.
  • backend/features/medicatiebeoordeling/tasks.py - Celery-tasks (run_review_task, archive_old_review_task, exports, gesprek-pipeline).
  • backend/features/medicatiebeoordeling/encryption.py - veld-encryptie.
  • backend/features/medicatiebeoordeling/archive.py - archivering naar S3.
  • backend/features/medicatiebeoordeling/dsar.py - DSAR-export per patiëntprofiel.
  • backend/features/medicatiebeoordeling/consent.py - opname- en review-consent-attestaties.
  • backend/features/medicatiebeoordeling/atlas_helpers.py, patient_services.py - hulpfuncties voor de in-context Atlas-chat en patiëntmutaties.
  • backend/features/medicatiebeoordeling/ratelimit.py - throttling per endpoint.
  • backend/features/medicatiebeoordeling/letter_providers/ - integratie voor briefverzending (uitnodigingen).
  • backend/features/medicatiebeoordeling/management/ - management-commands waaronder seed_standaardvragen.
  • backend/features/medicatiebeoordeling/migrations/ - Django-migraties.
  • backend/features/medicatiebeoordeling/tests/ - unit- en integratietests.

Belangrijke bestanden

  • backend/features/medicatiebeoordeling/models.py
  • backend/features/medicatiebeoordeling/services.py
  • backend/features/medicatiebeoordeling/views.py
  • backend/features/medicatiebeoordeling/urls.py
  • backend/features/medicatiebeoordeling/lookup.py
  • backend/features/medicatiebeoordeling/jansen.py
  • backend/features/medicatiebeoordeling/parsers/ais/
  • backend/features/medicatiebeoordeling/analyses/
  • backend/features/medicatiebeoordeling/data/lookup.db
  • backend/features/medicatiebeoordeling/consumers.py
  • backend/features/medicatiebeoordeling/tasks.py

API & Communicatie (Swagger)