📖 English version available: See README_english.md.
Plateforme minimaliste permettant à des personnes non techniques de produire des questionnaires cliniques ne stockant aucune information personnelle.
- Objectif Principal
- Description Technique
- Installation
- Utilisation
- Analytics (Optionnel)
- Configuration du questionnaire (YAML)
- Scripts de scoring
- Génération de rapports PDF
- Stockage des données structurées (si activé)
- Structure du projet
- Logs
- Exemple ProbaLYNCH
- Développement
- Licence
- Références et Crédits
PrevMed est conçu pour permettre aux professionnels de santé possédant quelques compétences minimales en informatique de créer facilement des questionnaires d'aide à la décision clinique avec un script (R ou Python) et un fichier .yaml.
Fonctionnement :
- Le patient remplit le questionnaire sur l'interface web
- Un PDF avec les réponses et résultats est généré instantanément
- Le patient vient en consultation avec ce PDF
- Aucune donnée personnelle n'est stockée sur le serveur
Ce système fait gagner du temps à tout le monde : le patient prépare ses réponses en amont et le clinicien dispose immédiatement d'informations structurées, voire de scores calculés automatiquement, comme dans l’application ProbaLynch, exemple d’application de PrevMed.
PrevMed permet de créer des questionnaires cliniques interactifs à partir de fichiers de configuration YAML. Le système génère automatiquement une interface web avec Gradio, gère la logique conditionnelle des questions, exécute des scripts de scoring (R ou Python) et produit des rapports PDF.
Caractéristiques principales :
- ✨ Configuration déclarative via YAML
- 🔀 Questions conditionnelles (affichage dynamique basé sur les réponses précédentes)
- 📊 Support de scripts de scoring en R (via rpy2) ou Python
- 🖥️ Interface web intuitive avec Gradio
- 📄 Génération automatique de rapports PDF
- 📝 Logging détaillé avec loguru
- 🎯 Type hints et documentation NumPy style
- 🔒 Polices système uniquement — aucune requête vers Google Fonts ou tout autre serveur externe (respect de la vie privée)
PrevMed peut être installé de plusieurs façons selon vos besoins :
La méthode la plus simple pour une utilisation en production :
# Installer PrevMed depuis PyPI
uv pip install PrevMed
# Ou avec pip traditionnel
pip install PrevMedPrérequis :
- Python 3.13.5 (ou version compatible)
- R et rpy2 si vous utilisez des scripts de scoring en R :
sudo apt install r-base - Sur Ubuntu 22.04, vous pourriez avoir besoin de :
sudo apt-get install libtirpc-dev(source)
Note : Vous devrez toujours cloner le dépôt ou télécharger les exemples séparément pour accéder aux fichiers YAML d'exemple et aux scripts de scoring.
Pour le développement ou la personnalisation :
# Cloner le dépôt
git clone https://github.com/PrevMedOrg/PrevMed
cd PrevMed
# Créer et activer un environnement virtuel
uv venv
source .venv/bin/activate # Sur Windows : .venv\Scripts\activate
# Installer en mode éditable
uv pip install -e .Prérequis : Identiques à la Méthode 1
Pour un déploiement en production conteneurisé sans code source local :
# Cloner le dépôt (seulement nécessaire pour docker-compose.yml et les exemples)
git clone https://github.com/PrevMedOrg/PrevMed
cd PrevMed
# Accéder au répertoire docker
cd docker
# Modifier docker-compose.yml pour définir INSTALL_MODE à "pypi"
# Changer la ligne: INSTALL_MODE: local
# En: INSTALL_MODE: pypi
# Optionnellement modifier la section 'command' pour spécifier les arguments souhaités
# Par exemple: --survey-yaml, --scoring-script, --save-user-data, etc.
# Lancer le conteneur en mode détaché (construit et installe depuis PyPI)
sudo docker compose up --build -dNote : Le répertoire examples sera monté depuis votre clone local. Vous pouvez également fournir vos propres fichiers YAML et scripts de scoring en modifiant les montages de volumes dans docker-compose.yml.
Pour un développement conteneurisé avec le code source local :
# Cloner le dépôt
git clone https://github.com/PrevMedOrg/PrevMed
cd PrevMed
# Accéder au répertoire docker
cd docker
# S'assurer que INSTALL_MODE est défini à "local" dans docker-compose.yml (c'est la valeur par défaut)
# Optionnellement modifier la section 'command' pour spécifier les arguments souhaités
# Par exemple: --survey-yaml, --scoring-script, --save-user-data, etc.
# Lancer le conteneur en mode détaché (construit depuis les sources locales)
sudo docker compose up --build -dGestion des volumes :
- Les dossiers
logs/etsurvey_data/sont montés comme volumes pour persister les données entre redémarrages - Les PDFs sont générés en mémoire par défaut (pas de fichiers sur disque), assurant une confidentialité maximale
Sécurité du conteneur Docker :
- Le conteneur s'exécute avec l'utilisateur non privilégié
nobody(pas de root) - Le système de fichiers du conteneur est en lecture seule (
read_only: true), sauf pour les volumes montés (logs/etsurvey_data/) - Ces mesures suivent les bonnes pratiques de sécurité Docker et réduisent la surface d'attaque
Cette configuration permet de bénéficier de l'isolation Docker tout en conservant les logs et données importantes, sans créer de fichiers temporaires sur le disque.
prevmed --survey-yaml <chemin_yaml> --scoring-script <chemin_script>Le projet inclut un exemple complet du questionnaire ProbaLYNCH (cf Références et Crédits) pour l'évaluation du risque de syndrome de Lynch :
prevmed --survey-yaml examples/ProbaLYNCH/ProbaLYNCH.yaml --scoring-script examples/ProbaLYNCH/ProbaLYNCH.RCeci lancera une interface Gradio accessible via votre navigateur web.
PrevMed supporte plusieurs options pour personnaliser le comportement de l'application :
Par défaut, aucune donnée utilisateur n'est sauvegardée. Les rapports PDF sont générés en mémoire et brièvement écrits dans /dev/shm (RAM, chmod 600) uniquement pour le téléchargement par le patient — aucun fichier n'est jamais écrit sur disque, aucune réponse ni résultat n'est loggé.
Pour sauvegarder les données utilisateur de manière permanente (dans le répertoire survey_data/), utilisez l'option --save-user-data :
prevmed --survey-yaml examples/ProbaLYNCH/ProbaLYNCH.yaml \
--scoring-script examples/ProbaLYNCH/ProbaLYNCH.R \
--save-user-dataAvec --save-user-data activé, les données suivantes sont sauvegardées :
- Fichiers JSON compressés (
.json.gz) contenant toutes les réponses et résultats - Logs CSV centralisés pour analyse rapide
- Rapports PDF stockés de manière permanente dans
survey_data/
Sans cette option (comportement par défaut) :
- Les PDFs sont générés en mémoire et brièvement écrits dans
/dev/shm(RAM, chmod 600) pour le téléchargement — aucun fichier écrit sur disque - Aucune donnée n'est loggée dans les fichiers CSV
- Aucun fichier JSON n'est sauvegardé
- Respect maximal de la vie privée des patients - empreinte disque nulle
# Ouvrir automatiquement le navigateur au démarrage
prevmed --survey-yaml <yaml> --scoring-script <script> --open-browser
# Utiliser un port personnalisé (par défaut: 7860)
prevmed --survey-yaml <yaml> --scoring-script <script> --port 8080
# Activer le logging de niveau debug dans la console
prevmed --survey-yaml <yaml> --scoring-script <script> --debug
# Spécifier l'URL réelle où le questionnaire est hébergé (apparaîtra dans les PDFs)
prevmed --survey-yaml <yaml> --scoring-script <script> --actual-url "https://survey.hopital.fr/probalynch"
# Afficher les mentions légales RGPD en bas de page (élément <details> repliable)
# ⚠️ OBLIGATOIRE : l'application refuse de démarrer sans ce fichier.
# Ce fichier doit contenir au minimum : responsable du traitement, finalités,
# durée de conservation et droits des personnes concernées (RGPD art. 13/14).
prevmed --survey-yaml <yaml> --scoring-script <script> --terms-md mentions_legales.mdPrevMed permet de passer n'importe quel argument supporté par Gradio directement à demo.launch(). Tous les arguments non reconnus par PrevMed sont automatiquement transmis à Gradio.
Formats supportés :
# Arguments avec valeur (chaîne, int, float)
prevmed --survey-yaml <yaml> --scoring-script <script> --gradio-option value
# Drapeaux booléens (True)
prevmed --survey-yaml <yaml> --scoring-script <script> --enable-feature
# Drapeaux booléens (False)
prevmed --survey-yaml <yaml> --scoring-script <script> --no-disable-featureExemples pratiques :
# Désactiver la fermeture automatique du serveur après inactivité
prevmed --survey-yaml <yaml> --scoring-script <script> --prevent-thread-lock
# Activer le mode favicon personnalisé
prevmed --survey-yaml <yaml> --scoring-script <script> --favicon-path /path/to/favicon.ico
# Combiner plusieurs arguments
prevmed --survey-yaml <yaml> --scoring-script <script> \
--max-file-size 10000000 \
--allowed-paths /data /images \
--no-show-errorNote : Consultez la documentation Gradio Blocks pour la liste complète des arguments supportés par demo.launch().
PrevMed inclut des options pour optimiser les performances sous charge importante :
# Augmenter le nombre maximum de threads (par défaut: 40)
prevmed --survey-yaml <yaml> --scoring-script <script> --max-threads 100
# Désactiver la file d'attente des requêtes (activée par défaut)
prevmed --survey-yaml <yaml> --scoring-script <script> --no-queueNote : La file d'attente (queue) est activée par défaut car elle améliore les performances sous charge. Pour plus d'informations sur l'optimisation des performances de Gradio, consultez le guide officiel : Setting Up a Demo for Maximum Performance.
PrevMed supporte l'intégration de Umami, une solution d'analytics respectueuse de la vie privée, self-hostable, open-source, respectant le RGPD et avec une option gratuite.
Note de confidentialité: Umami est configuré par défaut pour respecter le paramètre Do Not Track (DNT) du navigateur - les utilisateurs qui ont activé DNT dans leur navigateur ne seront pas suivis. Cette option peut être modifiée avec le paramètre --umami-ignore-dnt si nécessaire.
Pour activer l'analytics, utilisez les arguments de ligne de commande --umami-website-id et optionnellement --umami-url :
# Option 1: Utiliser le service cloud gratuit Umami (cloud.umami.is)
prevmed --survey-yaml examples/ProbaLYNCH/ProbaLYNCH.yaml \
--scoring-script examples/ProbaLYNCH/ProbaLYNCH.R \
--umami-website-id "votre-website-id"
# Option 2: Utiliser votre propre instance Umami auto-hébergée
prevmed --survey-yaml examples/ProbaLYNCH/ProbaLYNCH.yaml \
--scoring-script examples/ProbaLYNCH/ProbaLYNCH.R \
--umami-url "https://votre-instance.example.com" \
--umami-website-id "votre-website-id"Exemple complet :
# Avec cloud.umami.is (gratuit)
prevmed --survey-yaml examples/ProbaLYNCH/ProbaLYNCH.yaml \
--scoring-script examples/ProbaLYNCH/ProbaLYNCH.R \
--umami-website-id "70991a3f-4cc9-49ae-a848-867bc75a1fd1"
# Avec instance auto-hébergée
prevmed --survey-yaml examples/ProbaLYNCH/ProbaLYNCH.yaml \
--scoring-script examples/ProbaLYNCH/ProbaLYNCH.R \
--umami-url "https://analytics.monhopital.fr" \
--umami-website-id "70991a3f-4cc9-49ae-a848-867bc75a1fd1"
# Pour ignorer les préférences Do Not Track du navigateur (non recommandé)
prevmed --survey-yaml examples/ProbaLYNCH/ProbaLYNCH.yaml \
--scoring-script examples/ProbaLYNCH/ProbaLYNCH.R \
--umami-website-id "70991a3f-4cc9-49ae-a848-867bc75a1fd1" \
--umami-ignore-dntOptions disponibles :
--umami-website-id: ID du site web Umami (requis pour activer l'analytics)--umami-url: URL de l'instance Umami (optionnel, par défaut utilise cloud.umami.is)--umami-ignore-dnt: Ignorer les préférences Do Not Track du navigateur (optionnel, non recommandé pour des raisons de confidentialité)
Note : Si aucun argument analytics n'est fourni, l'application fonctionne sans analytics.
Les questionnaires sont définis dans des fichiers YAML avec la structure suivante :
survey_name: Nom du questionnaire
# Uniquement pour être mentionnée dans les logs etc:
survey_version: 1.0.0
PrevMed_version: 1.0.0
# Optionnel: titre personnalisé affiché en haut à gauche de la page
# Si défini, remplace le survey_name comme titre
# Si commence par # ou <: traité comme du markdown (et donc permettant le HTML aussi)
# Sinon: ajout automatique de `# ` avant de l'afficher comme du markdown.
page_title: Mon questionnaire
# Optionnel: afficher survey_name comme titre en haut de la page (par défaut: true)
# Ignoré si page_title est défini
show_survey_title: true
# Optionnel: afficher la version du questionnaire sur la page web et dans le PDF (par défaut: true)
show_survey_version: true
# Optionnel: afficher la version de la webapp sur la page web et dans le PDF (par défaut: true)
show_webapp_version: true
# Optionnel: contenu Markdown de la page d'accueil.
# Quand défini, une page d'accueil est affichée en premier sur la route /
# avec ce contenu et les mentions légales en bas. L'utilisateur clique sur
# "Commencer" pour accéder au questionnaire.
greetings_md: |
# Bienvenue
Description de la page d'accueil...
# Optionnel: chemin de la route pour la page du questionnaire quand
# greetings_md est défini. La page d'accueil est servie sur /,
# le questionnaire sur cette route. Par défaut: survey
# IMPORTANT: ne pas mettre de / dans la valeur (ex: "lynch", pas "/lynch")
survey_route: mon_questionnaire
# Optionnel: texte d'en-tête affiché en haut du questionnaire (format Markdown, avec support HTML)
header: |
## À propos de ce questionnaire
Description du questionnaire...
# Optionnel: texte Markdown affiché juste avant les questions (après le header et les versions)
questions_header: |
Veuillez répondre aux questions suivantes.
# Optionnel: texte du résumé de la section légale (par défaut: "LEGAL")
# Utilisé comme label du <details><summary> pour les mentions légales (--terms-md)
legal_summary: "Mentions légales et contact"
# Optionnel: contenu Markdown supplémentaire inclus dans le rapport PDF généré
# Supporte: titres (#, ##, ###), gras (**texte**), italique (*texte*), liens [texte](url)
pdf_extra_content: |
## Informations importantes
Ceci est un texte **en gras** et *en italique*.
Pour plus d'informations, visitez [notre site](https://example.com).
questions:
- variable: nom_variable
order: 1
widget: Radio|Number|Checkbox|Textbox
widget_args:
# Arguments spécifiques au widget
choices: ["Option1", "Option2"] # Pour Radio
precision: 0 # Pour Number
step: 1 # Pour Slider
label: "Texte du widget" # Optionnel: par défaut utilise question
question: "Texte de la question"
skip_if: "(nom_variable == 2) and (nom_variable > autre_variable)" # Si l'expression vaut True alors la question n'est pas posée (l'expression doit être en Python et a accès aux variables du reste du script.)Par principe, PrevMed devrait marcher avec n'importe quel widget Gradio. La liste des widgets Gradio est disponible ici. Les widgets suivants sont les plus utilisés:
- Radio : Boutons radio pour choix unique
choices: Liste des options (requis)
- Number : Champ numérique avec contrôles
- Checkbox : Case à cocher booléenne
- Textbox : Champ texte libre
PrevMed supporte deux types de logique conditionnelle :
Les questions peuvent être affichées ou masquées dynamiquement via le champ skip_if. Les conditions sont des expressions Python évaluées avec les valeurs des variables précédentes :
- variable: age_diagnostic
skip_if: "not (diagnostic_positif == True)"
# Cette question ne s'affiche que si diagnostic_positif n'est pas True
- variable: age_crc_proband
skip_if: "personal_crc_count == 0"
# Cette question est ignorée si le patient n'a aucun cancer colorectalPoints importants :
- L'expression retourne
True→ la question est ignorée - L'expression retourne
False→ la question est affichée - Les expressions peuvent utiliser toutes les variables des questions précédentes
- Les opérateurs Python standards sont supportés (
==,!=,>,<,and,or,not, etc.)
Les réponses peuvent être validées avant de passer à la question suivante via le champ valid_if. Si la validation échoue, un message d'erreur est affiché et l'utilisateur doit corriger sa réponse :
- variable: current_age
widget: Number
question: "Âge actuel du patient (en années)"
valid_if: "current_age >= 15 and current_age <= 120"
invalid_message: "L'âge doit être compris entre 15 et 120 ans."
- variable: personal_crc_count
widget: Number
question: "Combien de cancers colorectaux ?"
valid_if: "personal_crc_count >= 0"
invalid_message: "Le nombre de cancers ne peut pas être négatif."Points importants :
- L'expression retourne
True→ la réponse est valide, on peut continuer - L'expression retourne
False→ la réponse est invalide, un avertissement est affiché - Le champ
invalid_message(optionnel) permet de personnaliser le message d'erreur - Si
invalid_messagen'est pas fourni, un message par défaut est utilisé - La validation s'exécute avant de passer à la question suivante
Le script R doit définir une fonction scoring() qui prend les variables du questionnaire comme arguments nommés et retourne une liste avec 3 éléments :
- Une chaîne de caractères contenant du markdown à afficher au patient
- Une liste de listes représentant une table (première liste = headers, suivantes = lignes de données)
- Une liste nommée avec les options PDF (
include_md_in_pdfetinclude_data_in_pdf)
scoring <- function(variable1, variable2 = NULL, ...) {
# Logique de calcul
score_total <- 0.40
# Générer le texte markdown à afficher au patient sur l'interface web
# Ce texte est TOUJOURS affiché au patient directement dans son navigateur
markdown_result <- sprintf("## Résultats\n\nVotre score total est: %.1f%%", score_total * 100)
# Créer la table de données (format: liste de listes)
# Première liste = headers, suivantes = lignes de données
table_data <- list(
c("Catégorie", "Probabilité"), # Headers
c("Catégorie 1", sprintf("%.2f%%", 0.15 * 100)),
c("Catégorie 2", sprintf("%.2f%%", 0.25 * 100)),
c("Total", sprintf("%.2f%%", 0.40 * 100))
)
# Options de génération PDF
pdf_options <- list(
include_md_in_pdf = TRUE, # Inclure aussi le markdown dans le PDF (il est toujours affiché sur l'interface web)
include_data_in_pdf = TRUE # Inclure la table de données dans le PDF
)
# Retourner une liste avec 3 éléments
list(
markdown_result, # Élément 1: texte markdown
table_data, # Élément 2: table de données
pdf_options # Élément 3: options PDF
)
}Points importants :
- Les paramètres conditionnels doivent avoir
= NULLcomme valeur par défaut - Retourner une liste avec 3 éléments : markdown, table_data, pdf_options
- Le premier élément est une chaîne markdown qui est toujours affichée au patient sur l'interface web. L'option
include_md_in_pdfcontrôle si ce même texte est aussi inclus dans le rapport PDF (supporte le formatage markdown complet - voir ci-dessous) - Le deuxième élément est une liste de listes où la première liste contient les headers et les suivantes les lignes de données
- Le troisième élément contrôle ce qui est inclus dans le PDF (markdown et/ou table) - notez que le markdown est toujours affiché sur la page web indépendamment de ce paramètre
- Les noms de paramètres doivent correspondre aux noms de variables du YAML
Le script Python doit définir une fonction scoring() qui retourne un tuple avec 3 éléments :
- Une chaîne contenant du markdown à afficher au patient
- Une liste de listes représentant une table (première liste = headers, suivantes = lignes de données)
- Un dictionnaire avec les options PDF (
include_md_in_pdfetinclude_data_in_pdf)
def scoring(variable1: str, variable2: int = None, **kwargs) -> tuple[str, list[list[str]], dict[str, bool]]:
"""Calcul du score."""
# Logique de calcul
score_total = 0.40
# Générer le texte markdown à afficher au patient sur l'interface web
# Ce texte est TOUJOURS affiché au patient directement dans son navigateur
markdown_result = f"## Résultats\n\nVotre score total est: {score_total * 100:.1f}%"
# Créer la table de données (format: liste de listes)
# Première liste = headers, suivantes = lignes de données
table_data = [
["Catégorie", "Probabilité"], # Headers
["Catégorie 1", f"{0.15 * 100:.2f}%"],
["Catégorie 2", f"{0.25 * 100:.2f}%"],
["Total", f"{0.40 * 100:.2f}%"]
]
# Options de génération PDF
pdf_options = {
"include_md_in_pdf": True, # Inclure aussi le markdown dans le PDF (il est toujours affiché sur l'interface web)
"include_data_in_pdf": True # Inclure la table de données dans le PDF
}
# Retourner un tuple avec 3 éléments
return (markdown_result, table_data, pdf_options)Points importants :
- Retourner un tuple avec 3 éléments : markdown, table_data, pdf_options
- Le premier élément est une chaîne markdown qui est toujours affichée au patient sur l'interface web. L'option
include_md_in_pdfcontrôle si ce même texte est aussi inclus dans le rapport PDF (supporte le formatage markdown complet - voir ci-dessous) - Le deuxième élément est une liste de listes où la première liste contient les headers et les suivantes les lignes de données
- Le troisième élément contrôle ce qui est inclus dans le PDF (markdown et/ou table) - notez que le markdown est toujours affiché sur la page web indépendamment de ce paramètre
- Les valeurs de la table peuvent être de n'importe quel type (elles seront converties en chaînes automatiquement)
- Les noms de paramètres doivent correspondre aux noms de variables du YAML
Le champ markdown retourné par les scripts de scoring supporte les éléments suivants :
- Titres :
#,##,###, etc. (niveaux arbitraires) - Gras :
**texte** - Italique :
*texte* - Liens :
[texte](url)(les liens avec URL vide sont ignorés pour éviter les erreurs) - Tables : format pipe standard
- HTML : les balises HTML sont supportées dans les champs Markdown (ex :
<h1 style="text-align: center;">,<details>,<summary>, etc.)
Exemple de table dans le markdown :
| Catégorie | Valeur |
|-----------|--------|
| A | 10% |
| B | 20% |Les rapports PDF sont générés automatiquement à la fin du questionnaire et incluent :
- Nom et version du questionnaire
- Code de référence unique (format XXX-YYY, facile à mémoriser)
- Horodatage de génération
- Résultats du scoring (texte formaté + tableau structuré)
- Toutes les réponses aux questions
Approche de stockage des PDFs :
- Par défaut : les PDFs sont générés en mémoire et servis via un fichier temporaire dans
/dev/shm(tmpfs RAM — aucune écriture sur disque) - Avec
--save-user-data: les PDFs sont sauvegardés de manière permanente danssurvey_data/en plus des données JSON
Fonctionnement de la génération en mémoire :
Lorsque --save-user-data n'est pas activé (comportement par défaut) :
- Le PDF est généré directement en mémoire en utilisant le buffer BytesIO de Python
- Les octets sont écrits dans un fichier temporaire dans
/dev/shm(système de fichiers RAM de Linux — ne touche jamais le disque), avec les permissions600(lecture réservée au propriétaire) pour empêcher d'autres utilisateurs d'y accéder - Le DownloadButton de Gradio sert le fichier au navigateur du patient (il requiert un chemin de fichier, pas un objet BytesIO)
- Le fichier temporaire est automatiquement supprimé après un court délai une fois la fenêtre de téléchargement passée
- Aucun fichier n'est jamais écrit sur disque - protection maximale de la vie privée
Exemple de workflow :
- Patient complète le questionnaire à 14h00
- PDF généré en mémoire (BytesIO)
- PDF écrit dans
/dev/shm(RAM uniquement, chmod 600) pour être servi par Gradio - Patient télécharge le PDF
- Fichier temporaire supprimé de la RAM — aucune trace ne reste sur le disque
Cette approche assure une empreinte disque nulle et une confidentialité maximale des patients : les données ne résident qu'en RAM.
Lorsque l'option --save-user-data est activée, PrevMed sauvegarde toutes les données sous deux formats complémentaires :
Avec --save-user-data, chaque soumission est sauvegardée en JSON compressé (.json.gz) dans survey_data/ avec :
- Nom du questionnaire et versions
- Réponses complètes
- Résultats de scoring
- Code de référence unique
- Timestamp Unix
- Hashes de client (pour détection de doublons anonyme)
Format du nom de fichier : {timestamp}_{reference_code}_{unique_id}.json.gz
Exemple : 1729500000_A2B-3C4_a1b2c3d4.json.gz
Note : Le {unique_id} est un fragment UUID (premiers 8 caractères) qui garantit l'unicité absolue même en cas de collision d'horodatage.
Avec --save-user-data, un fichier CSV centralisé enregistre toutes les soumissions pour analyse rapide :
Emplacement : survey_data/csv/{PrevMed_version}/{survey_name}_{survey_version}/survey_submissions.csv
Exemple : survey_data/csv/0.8.0/ProbaLYNCH_1.0.0/survey_submissions.csv
Structure des colonnes :
- Colonnes fixes :
reference_code,row_number,timestamp_unix,datetime - Colonnes de scoring : une par résultat (ex:
p_MLH1,p_MSH2, formatées en pourcentages) - Colonnes de hashes :
answers_hash+ hashes individuels par attribut client (ex:user_agent_hash,ip_address_hash)
Gestion de la concurrence :
- Utilise
filelockpour garantir l'atomicité des écritures - Supporte l'accès concurrent depuis plusieurs processus/serveurs
- Timeout de 10 secondes sur le verrou
Rotation automatique :
- Le CSV est automatiquement archivé après 1000 lignes
- Fichier archivé :
survey_submissions_{timestamp}.csv(sauvegarde permanente) - Nouveau CSV créé automatiquement pour continuer l'enregistrement
- Objectif : maintenir des performances élevées même avec accès concurrent intensif
Gestion des erreurs :
- En cas de timeout du verrou (charge très élevée), les données sont sauvegardées dans un fichier de secours
- Format :
survey_submissions_fallback_{timestamp}_{uuid}.csv(où{uuid}est un fragment UUID de 8 caractères) - Garantie : aucune perte de données même sous charge extrême
Détection de doublons :
answers_hash: hash court (12 caractères) des réponses uniquement- Hashes individuels de client : chaque attribut (user-agent, IP, etc.) haché séparément avec le code de référence comme sel
- Permet l'analyse de doublons tout en préservant la vie privée
Exemple de contenu CSV :
reference_code,row_number,timestamp_unix,datetime,p_MLH1,p_MSH2,p_MSH6,p_PMS2,p_total,answers_hash,user_agent_hash,ip_address_hash,session_hash_hash
A2B-3C4,1,1729500000,2024-10-21 14:20:00,15.23,25.47,8.92,10.38,60.00,a1b2c3d4e5f6,x9y8z7w6v5u4,q1w2e3r4t5y6,m1n2b3v4c5x6
D5E-6F7,2,1729500120,2024-10-21 14:22:00,2.15,3.28,1.45,1.12,8.00,f6e5d4c3b2a1,u4v5w6z7y8x9,y6t5r4e3w2q1,x6c5v4b3n2m1Avantages de cette architecture :
- Performance : rotation limite la taille des fichiers pour maintenir vitesse d'accès
- Fiabilité : système de fallback garantit zéro perte de données
- Traçabilité : archivage automatique avec timestamps
- Analyse : format CSV facilite l'analyse statistique rapide
- Scalabilité : gestion de concurrence permet déploiement multi-processus/multi-serveurs
- Vie privée : hashes salés permettent détection de doublons sans stocker données personnelles brutes
PrevMed/
├── src/
│ ├── __init__.py # Setup du logging
│ ├── __main__.py # Point d'entrée CLI
│ └── utils/
│ ├── gui/ # Interface Gradio (package)
│ │ ├── __init__.py # Réexporte create_survey_interface
│ │ ├── greetings.py # Page d'accueil (greetings_md + mentions légales)
│ │ └── survey.py # Questionnaire (questions, scoring, PDF)
│ ├── css.py # Le CSS utilisé dans Gradio
│ ├── js.py # Le js utilisé dans Gradio
│ ├── io.py # Chargement YAML et scripts
│ ├── logic.py # Logique conditionnelle
│ ├── pdf.py # Génération PDF
│ ├── scoring.py # Exécution scripts R/Python
│ └── settings.py # Stocke des variables disponibles dans tout le script
├── examples/
│ └── ProbaLYNCH/
│ ├── ProbaLYNCH.yaml # Configuration ProbaLYNCH
│ └── ProbaLYNCH.R # Script de scoring ProbaLYNCH
├── logs/ # Logs rotatifs (créé automatiquement)
├── survey_pdfs/ # Rapports PDF (créé automatiquement)
├── requirements.txt
└── setup.py
Les logs sont automatiquement sauvegardés dans ./logs/ avec rotation journalière et rétention de 30 jours. Le format inclut :
- Horodatage avec millisecondes
- Niveau de log
- Fichier, fonction et ligne
- Message
Par défaut, les logs fichiers sont au niveau INFO. Pour activer le niveau DEBUG (plus verbeux), définissez la variable d'environnement PREVMED_LOG_LEVEL :
PREVMED_LOG_LEVEL=DEBUG prevmed --survey-yaml <yaml> --scoring-script <script>Pour illustrer le fonctionnement de PrevMed, nous avons implémenté le questionnaire ProbaLYNCH (preventionfamiliale.fr) (cf. Références et Crédits).
Le questionnaire ProbaLYNCH évalue le risque de mutations dans les gènes MLH1, MSH2, MSH6 et PMS2 (syndrome de Lynch) basé sur :
- L'histoire personnelle de cancers (colorectal, utérus (« endomètre »), autres)
- Les âges au diagnostic
- L'histoire familiale (apparentés proches)
Le modèle ProbaLYNCH utilise un modèle de régression logistique multinomiale avec transformation softmax pour calculer les probabilités de mutation pour chaque gène d'une liste prédéterminée de gènes. Une adaptation en langage R a été écrite en 2025 par Laury NICOLAS, médecin du CHU de Pointe-à-Pitre, quand il était Dr Junior dans le Service GENOAP (Génétique-Oncogénétique-Adulte-Prévention) du CHU de Clermont-Ferrand. Ce script R à ensuite été optimisé par madame Anna Serova-Erard, Conseillère en Génétique et Médecine Prédictive à GENOAP.
- Type hints et docstrings NumPy style partout
- Commentaires explicites pour les décisions de design
- Utilisation de
logurupour le logging - Code simple et robuste privilégié
- Utilisation de Ruff comme linter pour assurer la qualité du code
Ce projet a été développé avec l'assistance de aider.chat et Claude Code.
Nous accueillons volontiers :
- 🐛 Les signalements de bugs via les Issues
- ✨ Les demandes de fonctionnalités
- 🔧 Les Pull Requests pour améliorer le projet
N'hésitez pas à contribuer !
Actuellement sous licence GNU Affero General Public License. Cependant, nous sommes flexibles et ouverts à des arrangements de licence alternatifs - n'hésitez pas à nous contacter si vous avez besoin d'une licence différente pour votre cas d'usage.
- Afin de permettre à tout un chacun d’évaluer sa probabilité d’être porteur du risque génétique, l’association de soutien à la génétique épidémiologique ASAGE (loi 1901 n°RNA W751217490, J.O. du 15 décembre 2012), met à disposition libre, gratuite et sans stockage d’aucune donnée personnelle, l’application ProbaLYNCH (sous licence GNU Affero General Public License) : le code informatique montrant l’absence de conservation des données est accessible en ligne et tourne chez un hébergeur français. L’application initiale a été codée en langage informatique R en 2025, quand il était Dr Junior dans le Service GENOAP (Génétique-Oncogénétique-Adulte-Prévention) du CHU de Clermont-Ferrand, par le Dr Laury NICOLAS, actuellement Chef de Clinique de l’Université des Antilles - Assistant des Hôpitaux au CHU de la Guadeloupe. Le script R a été optimisé à GENOAP par Madame Anna SEROVA-ERARD, Conseillère en Génétique et Médecine Prédictive. Les illustrations ont été réalisées par Madame Anne SPECQ, Conseillère en Génétique et Médecine Prédictive, ancienne stagiaire de GENOAP. L'application ProbaLynch a été finalisée par olicorp sur sa plateforme PrevMed, mise à disposition librement.
- Kastrinos et al, J Clin Oncol 35, 2165-2172 (2017). DOI: 10.1200/JCO.2016.69.6120. « Development and Validation of the PREMM5 Model for Comprehensive Risk Assessment of Lynch Syndrome ».