Aller au contenu

Messages PMSI

pmsi

evenementsPMSI / RSS MCO depuis des tables — un fichier par événement.

evenements (1 ligne = 1 fichier ; colonnes-chemins relatives à evenementPMSI) obligatoires : evt_id, ipp, sexe, date_naissance, nda, venue_date_entree facultatives : ipp_recepteur, nom_usuel, nom_naissance, prenom, nda_recepteur, venue_heure_entree, venue_mode_entree, venue_uf_responsable, venue_date_sortie, venue_heure_sortie, rss_emetteur, rss_recepteur, rss_version, numero_finess, identifiant_message, date_heure_production, commentaire

rum (1 ligne = 1 RUM ; colonnes-chemins relatives à rum) obligatoires : evt_id, rum_id, um, um_date_entree, um_mode_entree facultatives : rum_emetteur (défaut rum_id), rum_recepteur, action (creation), date_action (défaut um_date_entree), um_heure_entree, um_provenance, um_date_sortie, um_heure_sortie, um_mode_sortie, um_destination, igs2, nb_seances

diagnostics (format long ; colonnes-chemins relatives au diagnostic) evt_id, rum_id, type ∈ {DP, DR, DAS, DAD}, code

Alternative : colonnes ``dp``, ``dr``, ``das``, ``dad`` dans ``rum``. ``das`` et ``dad``
acceptent une liste (type List de polars) ou une chaîne séparée par des virgules
(``"E11.9, I10"``). Les deux formes peuvent se combiner, sans doublon de DP ou de DR.

actes (1 ligne = 1 acte CCAM ; colonnes-chemins relatives à acte) obligatoires : evt_id, rum_id, code, phase, activite facultatives : quantite (1), extension_pmsi, date_realisation, extension_documentaire, modificateurs (ex. "7J"), association_non_prevue, remboursement_exceptionnel, externe

rum_id n'est qu'une clé de jointure : l'identifiant XML du RUM est rum_emetteur (deux RUM d'un même RSS peuvent partager le même émetteur).

write_evenements_pmsi

write_evenements_pmsi(
    evenements: Table,
    rum: Table,
    diagnostics: Table | None = None,
    actes: Table | None = None,
    *,
    entete: dict | None = None,
    out_dir: str | Path,
    filename: str | Callable[[dict], str] = "{evt_id}.xml",
    version: str | None = None,
    xmlns: str | None = HPRIM_NS,
    encoding: str = "UTF-8",
    validate_xsd: str | bool = "error",
    colmap: dict | None = None,
    par_rum: bool = False,
    identifiant_egal_nom_fichier: bool = True,
    fichier_ok: bool = False,
) -> list[Path]

Écrit des fichiers evenementsPMSI (RSS MCO) depuis des tables.

Unité atomique du PMSI : le RUM, seul porteur de l'attribut action et d'un identifiant. par_rum=True écrit un message par RUM.

Les tables sont décrites en tête du module. Toute colonne dont le nom contient / ou commence par @ est une colonne-chemin.

Parameters:

Name Type Description Default
evenements Table

une ligne par séjour (polars ou pandas).

required
rum Table

une ligne par RUM.

required
diagnostics Table | None

format long, type dans DP, DR, DAS, DAD.

None
actes Table | None

une ligne par acte CCAM.

None
entete dict | None

emetteur, destinataire et en option emetteur_categorie, destinataire_categorie, mode_traitement, acquittement_attendu, commentaire, date_heure_production. Chaque clé peut aussi être une colonne de evenements (prioritaire).

None
out_dir str | Path

dossier de sortie (créé si besoin).

required
filename str | Callable[[dict], str]

gabarit formaté avec la ligne evenement, ou fonction ligne -> nom.

'{evt_id}.xml'
version str | None

version du schéma ; sinon colonne version, sinon 2.4.

None
xmlns str | None

HPRIM_NS (défaut), None (pas de namespace) ou "" (xmlns="").

HPRIM_NS
encoding str

encodage des fichiers.

'UTF-8'
validate_xsd str | bool

"error" (défaut), "warn" ou False.

'error'
colmap dict | None

renommage de colonnes {ancien: nouveau}.

None
par_rum bool

un fichier par RUM au lieu d'un par séjour. evt_id vaut alors {evt_id}_{rum_id} ; le gabarit dispose aussi de {rss_id} (ou {evt_origine}) et {rum_id}. Une colonne identifiant_message dans rum fixe l'identifiant de chaque message (12 caractères max).

False
identifiant_egal_nom_fichier bool

si vrai (défaut), l'identifiantMessage de chaque message est exactement le nom de son fichier sans extension (evt_id, ou {evt_id}_{rum_id} avec par_rum, ou ce que donne filename). Une colonne identifiant_message différente lève une erreur. False : identifiant et nom de fichier indépendants (utile pour réécrire des fichiers d'autres producteurs).

True
fichier_ok bool

dépose un fichier .ok vide, de même nom, une fois chaque .xml écrit intégralement (marqueur de dépôt complet pour le destinataire). Le .xml est toujours écrit de façon atomique (nom temporaire puis renommage).

False

Returns:

Type Description
list[Path]

La liste des fichiers écrits.

Raises:

Type Description
HprimWriteError

colonne obligatoire manquante, identifiantMessage invalide, ou message non conforme au XSD (avec validate_xsd="error").

Examples:

Script examples/exemple_pmsi.py :

# Régénère data/xml_tests/pmsi/00001.xml : un séjour (RSS) à deux RUM.
from datetime import date, datetime, time

import polars as pl

import pyhprimxml as hx

# En-tête du message (chaque clé peut aussi être une colonne de `evenements`)
entete = {
    "version": "2.00",                   # version HPRIM XML des messages
    "emetteur": "TEST",
    "destinataire": None,            # agent destinataire sans code, comme le fichier d'origine
    "mode_traitement": "reel",
    "acquittement_attendu": "oui",
    "commentaire": "HPRIM-TEST",
    "date_heure_production": datetime(2024, 1, 1, 1, 1, 1),
}

# Une ligne par séjour, donc par fichier : patient, venue, identifiant du RSS
evenements = pl.DataFrame({
    "evt_id": ["00001_pmsi"],    # -> fichier 00001_pmsi.xml, identifiantMessage 00001_pmsi
    "ipp": ["123456789"], "ipp_recepteur": ["123456789"],
    "sexe": ["M"], "nom_usuel": ["SANDWICK"], "nom_naissance": ["SANDWICK"], "prenom": ["JOHN"],
    "date_naissance": [date(1970, 1, 1)],
    "nda": ["987654321"], "nda_recepteur": ["987654321"],
    "venue_date_entree": [date(2024, 1, 1)], "venue_heure_entree": [time(8, 0)],
    "venue_mode_entree": ["8"], "venue_uf_responsable": ["4321"],
    "rss_emetteur": ["0"], "rss_recepteur": ["000"],
    # colonnes-chemins : attributs sans colonne dédiée
    "patient/@anonyme": ["non"], "patient/@confidentiel": ["non"], "venue/@prive": ["non"],
})

# Une ligne par RUM ; rum_id = rang du RUM dans le RSS (clé de jointure)
rum = pl.DataFrame({
    "evt_id": ["00001_pmsi", "00001_pmsi"],
    "rum_id": [1, 2],
    "rum_emetteur": ["pmsi_00001", "pmsi_00001"],     # identifiant XML du RUM
    "rum_recepteur": ["98765", "98766"],
    "date_action": [datetime(2024, 1, 1, 8, 10), datetime(2024, 1, 1, 9, 0)],
    "um": ["4321", "5000"],
    "um_date_entree": [date(2024, 1, 1)] * 2,
    "um_heure_entree": [time(8, 0), time(9, 0)],
    "um_mode_entree": ["8", "8"],
    "@classant": ["non", "non"],
    "@prestationInterEtablissementA": ["non", "non"],
    "medecinResponsable/identification/code": ["01234567"] * 2,
    "medecinResponsable/personne/nomUsuel": ["HOBBES"] * 2,
    "medecinResponsable/personne/prenoms/prenom": ["MARC-JACQUES"] * 2,
})

# Format long : une ligne par diagnostic, type dans DP, DR, DAS, DAD
diagnostics = pl.DataFrame({
    "evt_id": ["00001_pmsi"] * 3,
    "rum_id": [1, 1, 2],
    "type": ["DP", "DR", "DP"],
    "code": ["Z511", "C10", "Z743"],
})

# Une ligne par acte CCAM
actes = pl.DataFrame({
    "evt_id": ["00001_pmsi"] * 5,
    "rum_id": [1, 1, 1, 1, 2],
    "code": ["EBLA003", "EPLF002", "NFKA006", "YYYY033", "QZRP004"],
    "phase": [0] * 5, "activite": [1] * 5, "quantite": [1] * 5,
    "date_realisation": [datetime(2024, 1, 1, 8, 10)] * 4 + [datetime(2024, 1, 1, 9, 10)],
    "externe": ["non"] * 5, "remboursement_exceptionnel": ["non"] * 5,
})

fichiers = hx.write_evenements_pmsi(
    evenements, rum, diagnostics, actes,
    entete=entete,
    out_dir="sortie/pmsi",           # -> sortie/pmsi/00001_pmsi.xml
    validate_xsd="warn",             # destinataire sans code : non conforme au XSD
)

read_evenements_pmsi

read_evenements_pmsi(
    sources: Sources,
    *,
    extra: dict[str, list[str]] | None = None,
    version: str | None = None,
    backend: str = "polars",
    exiger_ok: bool = False,
    format_diagnostics: str = "long",
) -> dict[str, Table]

Lit des fichiers evenementsPMSI (RSS MCO) vers les tables de write_evenements_pmsi.

evt_id est le nom du fichier sans extension (suffixé _1, _2… si un fichier contient plusieurs événements) ; rum_id est le rang du RUM dans le RSS.

Parameters:

Name Type Description Default
sources Sources

fichier, dossier, motif glob, bytes ou liste.

required
extra dict[str, list[str]] | None

colonnes-chemins supplémentaires à extraire, par table, par exemple {"evenements": ["patient/@anonyme"], "rum": ["@classant"]}.

None
version str | None

forcer une version de schéma (sinon celle de chaque fichier).

None
backend str

"polars" (défaut) ou "pandas".

'polars'
format_diagnostics str

"long" (défaut) : table diagnostics, une ligne par code. "colonnes" : colonnes dp, dr (texte) et das, dad (listes) dans rum ; la table diagnostics est alors vide. Les deux formes se réécrivent telles quelles avec write_evenements_pmsi.

'long'
exiger_ok bool

ne lit que les .xml accompagnés de leur .ok (dépôt terminé) ; les autres sont ignorés, avec un avertissement.

False

Returns:

Type Description
dict[str, Table]

{"evenements", "rum", "diagnostics", "actes"}.

iter_evenements_pmsi

iter_evenements_pmsi(
    evenements: Table,
    rum: Table,
    diagnostics: Table | None = None,
    actes: Table | None = None,
    *,
    entete: dict | None = None,
    version: str | None = None,
    colmap: dict | None = None,
    par_rum: bool = False,
) -> Iterator[tuple[dict, dict]]

Itère sur (ligne evenement, arbre xmltodict) — un arbre par événement.

un message par RUM au lieu d'un message par séjour. L'événement

evt_id devient {evt_id}_{rum_id} (donc le nom de fichier par défaut aussi) ; patient, venue et identifiantRSS sont répétés dans chaque message.