Source code for bqss.bqss.models.documents
import collections
import datetime
import typing
from enum import Enum
# pylint: disable=no-name-in-module
from pydantic import BaseModel, root_validator, validator
##########################
# Document Etablissement #
##########################
[docs]
class TailleEnum(str, Enum):
"""
Valeurs possibles de taille d'établissement
"""
PETIT = "petit"
MOYEN = "moyen"
GRAND = "grand"
[docs]
class TypeEtablissementEnum(str, Enum):
"""
Valeurs possibles de type d'établissement
"""
PUBLIC = "Public"
PRIVE = "Privé"
PRIVE_NL = "Privé à but non lucratif"
CH = "CH"
CHU = "CHU"
CLCC = "CLCC"
[docs]
class ActiviteLibelleEnum(str, Enum):
"""
Valeurs possibles d'activité d'établissement
"""
MEDECINE = "Médecine"
SSR = "Soins de Suite et de Réadaptation"
SLD = "Soins de longue durée"
AMP_DPN = (
"Assistance médicale à la procréation - Diagnostic prénatal (AMP-DPN)"
)
CANCERO = "Cancérologie"
OBSTETRIQUE = "Obstétrique"
CHIRURIGE = "Chirurgie"
REANIMATION = "Réanimation"
NEPHROLOGIE = "Néphrologie"
PSYCHATRIE = "Psychiatrie"
IMAGERIE = "Imagerie Médicale"
DIAGNOSTIC_GENETIQUE = "Diagnostic génétique"
[docs]
class ActiviteIdEnum(str, Enum):
"""
Identifiants d'activité possibles
"""
MEDECINE = "medecine"
SSR = "ssr"
SLD = "sld"
AMP_DPN = "amp_dpn"
CANCERO = "cancerologie"
OBSTETRIQUE = "obstetrique"
CHIRURIGE = "chirurgie"
REANIMATION = "reanimation"
NEPHROLOGIE = "nephrologie"
PSYCHATRIE = "psychiatrie"
IMAGERIE = "imagerie"
DIAGNOSTIC_GENETIQUE = "diagnostic_genetique"
[docs]
class IQSSSourceEnum(str, Enum):
"""
Sources possibles des IQSS
"""
PMSI = "IQSS PMSI"
DOSSIER_PATIENT = "IQSS dossier patient"
QUESTIONNAIRE_ES = "IQSS questionnaire établissement"
[docs]
class ESATISSourceEnum(str, Enum):
"""
Sources possibles des IQSS questionnaire patient
"""
QUESTIONNAIRE_PA = "IQSS questionnaire patient"
[docs]
class IQSSSecteurEnum(str, Enum):
"""
Secteurs possibles pour les IQSS
"""
HAD = "HAD"
SSR = "SSR"
PSY = "PSY"
MHS = "MHS"
TCH = "TCH"
MCO = "MCO"
[docs]
class IQSSThemeEnum(str, Enum):
"""
Thèmes possibles pour les IQSS
"""
DIA = "DIA"
IDM = "IDM"
DPA = "DPA"
AVC = "AVC"
HPP = "HPP"
ETEORTHO = "ETEORTHO"
IAS = "IAS"
ISOORTHO = "ISOORTHO"
CA = "CA"
RCP = "RCP"
DAN = "DAN"
Q48H = "48h"
PCC = "PCC"
SMHOSP = "SMHOSP"
[docs]
class ESATISSecteurEnum(str, Enum):
"""
Secteurs possibles pour les IQSS questionnaire patient
"""
MCO = "MCO"
SSR = "SSR"
[docs]
class ESATISThemeEnum(str, Enum):
"""
Thèmes possibles pour les IQSS questionnaire patient
"""
CA = "CA"
Q48H = "48h"
SSR = "SSR"
[docs]
class Certif2021ScoreTypeEnum(str, Enum):
"""
Type de scores possibles pour la certification 2021
"""
DECISION = "decision"
CHAPITRE = "chapitre"
OBJECTIF = "objectif"
DATE_DECISION = "date_decision"
# pylint: disable=too-few-public-methods
[docs]
class FinessIdentificationModel(BaseModel):
"""
Données d'identification de l'établissement
"""
num_finess_ej: str
raison_sociale_et: str
raison_sociale_longue_et: typing.Optional[str]
raison_sociale_ej: str
raison_sociale_longue_ej: typing.Optional[str]
date_ouverture: datetime.date
statut_juridique: str
statut_juridique_ej: int
libelle_statut_juridique_ej: str
type_etablissement: TypeEtablissementEnum
[docs]
class FinessCoordonnesModel(BaseModel):
"""
Coordonnées de l'établissement
"""
adresse_postale_ligne_1: typing.Optional[str]
adresse_postale_ligne_2: str
libelle_commune: str
code_postal: str
departement: str
libelle_departement: str
telephone: typing.Optional[str]
latitude: typing.Optional[float]
longitude: typing.Optional[float]
[docs]
class FinessAutorisationModel(BaseModel):
"""
Données d'autorisation d'activité de l'établissement
"""
activite: int
libelle_activite: str
date_mise_en_oeuvre: typing.Optional[datetime.date]
[docs]
class FinessActivitesModel(BaseModel):
"""
Données d'activités pratiquées par l'établissement
"""
id: ActiviteIdEnum
libelle: ActiviteLibelleEnum
class IQSSVariableModel(BaseModel):
title: str
value: typing.Optional[typing.Union[float, str]]
missing_value: typing.Optional[str]
[docs]
class IQSSValueModel(BaseModel):
"""
Données d'un indicateur recueilli pour une année donnée et un établissement donné.
Un indicateur peut être constitué d'une ou plusieurs variables.
"""
annee_recueil: int
status: typing.Optional[IQSSVariableModel]
resultat: typing.Optional[IQSSVariableModel]
classe: typing.Optional[IQSSVariableModel]
denominateur: typing.Optional[IQSSVariableModel]
positionnement: typing.Optional[IQSSVariableModel]
evolution: typing.Optional[IQSSVariableModel]
score: typing.Optional[IQSSVariableModel]
intervalle_haut: typing.Optional[IQSSVariableModel]
intervalle_bas: typing.Optional[IQSSVariableModel]
controle: typing.Optional[IQSSVariableModel]
obligatoire: typing.Optional[IQSSVariableModel]
resultat_min: typing.Optional[IQSSVariableModel]
resultat_strict: typing.Optional[IQSSVariableModel]
effectif_cible: typing.Optional[IQSSVariableModel]
effectif_observe: typing.Optional[IQSSVariableModel]
effectif_attendu: typing.Optional[IQSSVariableModel]
capacite: typing.Optional[IQSSVariableModel]
volumetrie: typing.Optional[IQSSVariableModel]
def check_indicateur_unicity(values):
annee_count = collections.Counter(v.annee_recueil for v in values)
if annee_count.most_common()[0][1] > 1:
raise AssertionError("Année de recueil en doublon")
return values
[docs]
class FinessIQSSModel(BaseModel):
"""
Données des IQSS autres que le questionnaire satisfaction patient
"""
secteur: IQSSSecteurEnum
theme: IQSSThemeEnum
source: IQSSSourceEnum
indicateur: str
valeurs: typing.List[IQSSValueModel]
_annee_unicity = validator("valeurs", allow_reuse=True)(
check_indicateur_unicity
)
[docs]
class FinessESATISModel(BaseModel):
"""
Données des IQSS questionnaire statisfaction patient
"""
secteur: ESATISSecteurEnum
theme: ESATISThemeEnum
source: ESATISSourceEnum
indicateur: str
valeurs: typing.List[IQSSValueModel]
_annee_unicity = validator("valeurs", allow_reuse=True)(
check_indicateur_unicity
)
[docs]
class FinessCapaciteModel(BaseModel):
"""
Données de capacité d'activité de l'établissement pour une année
"""
annee: int
key: str
title: str
value: typing.Union[bool, float, str]
[docs]
class FinessVolumetrieModel(BaseModel):
"""
Données de volumétrie d'activité de l'établissement pour une année
"""
annee: int
key: str
title: str
value: float
[docs]
class FinessCertif2014Model(BaseModel):
"""
Données de certification du référentiel 2014-2020
"""
key: str
title: str
libelle_thematique: str
value: typing.Union[float, str, datetime.date]
[docs]
class FinessCertif2021Model(BaseModel):
"""
Données de certification du référentiel 2021-2025
"""
key: str
title: str
value: typing.Union[float, str, int]
score_type: typing.Optional[Certif2021ScoreTypeEnum]
chapitre_id: typing.Optional[str]
objectif_id: typing.Optional[str]
# pylint: disable=no-self-argument
[docs]
@root_validator
def check_certif_score_type_coherence(cls, values):
key = values.get("key")
score_type = values.get("score_type")
chapitre_id = values.get("chapitre_id")
objectif_id = values.get("objectif_id")
if "decision" in key and not "date_decision" in key:
if score_type != "decision":
raise ValueError(f"Incohérence ({score_type=}) et ({key=})")
if chapitre_id is not None:
raise ValueError(
f"({chapitre_id=}) alors qu'il devrait être `None`"
" pour une decision"
)
if objectif_id is not None:
raise ValueError(
f"({objectif_id=}) alors qu'il devrait être `None`"
" pour une decision"
)
if "chapitre" in key:
if score_type != "chapitre":
raise ValueError(f"Incohérence ({score_type=}) et ({key=})")
if objectif_id is not None:
raise ValueError(
f"({objectif_id=}) alors qu'il devrait être `None`"
" pour un chapitre"
)
expect_chapitre_id = key.rsplit("_", 1)[-1]
if expect_chapitre_id != chapitre_id:
raise ValueError(
f"{chapitre_id=} mauvaise valeure."
f" Valeure attendue: {expect_chapitre_id=}"
)
if "objectif" in key:
if score_type != "objectif":
raise ValueError(f"Incohérence ({score_type=}) et ({key=})")
expect_chapitre_id = key.rsplit("_", 1)[-1]
expect_chapitre_id, expect_objectif_id = expect_chapitre_id.split(
"."
)
if expect_chapitre_id != chapitre_id:
raise ValueError(
f"{chapitre_id=} mauvaise valeure."
f" Valeure attendue: {expect_chapitre_id=}"
)
if expect_objectif_id != objectif_id:
raise ValueError(
f"{chapitre_id=} mauvaise valeure."
f" Valeure attendue: {objectif_id=}"
)
return values
[docs]
class FinessDocumentModel(BaseModel):
"""
Données représentant un FINESS Géographique dans la base document
"""
finess_geo: str
taille: TailleEnum
ferme_cette_annee: bool
identification: FinessIdentificationModel
coordonnees: FinessCoordonnesModel
autorisations: typing.List[FinessAutorisationModel]
activites: typing.List[FinessActivitesModel]
IQSS: typing.List[FinessIQSSModel]
satisfaction_patient: typing.List[FinessESATISModel]
capacite: typing.List[FinessCapaciteModel]
volumetrie: typing.List[FinessVolumetrieModel]
certification_v2014: typing.List[FinessCertif2014Model]
certification_v2021: typing.Optional[typing.List[FinessCertif2021Model]]
# pylint: disable=no-self-argument
[docs]
@root_validator()
def check_no_empty_data(cls, values):
if (
not values["IQSS"]
and not values["satisfaction_patient"]
and not values["certification_v2014"]
and not values["certification_v2021"]
):
raise ValueError(
f"Aucune donnée HAS pour le finess {values['finess_geo']}"
)
return values