Commit ·
0eb1c65
1
Parent(s): 7062112
feat app
Browse files- src/page/acte.py +68 -0
- src/page/ctxt.py +18 -0
- src/page/germes.py +37 -0
- src/page/hello.py +27 -0
- src/page/intox.py +69 -0
- src/streamlit_app.py +15 -35
src/page/acte.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import unidecode
|
| 3 |
+
import polars as pl
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
GRIST_URL = "https://grist.numerique.gouv.fr/o/dim-chu-brest/api/docs/5ZXqTKBiHTD2qqUj6GVPtp" # ou ton serveur
|
| 8 |
+
TABLE = "Actes_classants"
|
| 9 |
+
|
| 10 |
+
records = requests.get(f"{GRIST_URL}/tables/{TABLE}/records").json()["records"]
|
| 11 |
+
|
| 12 |
+
acte = pl.DataFrame(records).drop("id").unnest("fields")
|
| 13 |
+
|
| 14 |
+
names = {
|
| 15 |
+
"CODE_ACTE": "Code acte",
|
| 16 |
+
"LIBELLE_ACTE": "Libellé acte",
|
| 17 |
+
"PARAGRAPHE": "Sous-Paragraphe",
|
| 18 |
+
"TECHNIQUE": "Technique",
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
items = (
|
| 22 |
+
acte.filter(pl.col("LIBELLE_CARACTERISTIQUE").is_not_null())["LIBELLE_CARACTERISTIQUE"]
|
| 23 |
+
.unique()
|
| 24 |
+
.str.to_titlecase()
|
| 25 |
+
.sort()
|
| 26 |
+
.to_list()
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
st.title("Actes CCAM")
|
| 30 |
+
|
| 31 |
+
# widgets
|
| 32 |
+
col1, col2, col3 = st.columns(3)
|
| 33 |
+
|
| 34 |
+
exp = col1.text_input("Mot(s) clef(s):", "")
|
| 35 |
+
exp = unidecode.unidecode(exp)
|
| 36 |
+
|
| 37 |
+
selection = col3.multiselect("Filtre(s) :", options=items, default=[])
|
| 38 |
+
|
| 39 |
+
_temp = (
|
| 40 |
+
acte.filter(
|
| 41 |
+
(pl.col("CODE_ACTE").str.contains(r"(?i)" + exp))
|
| 42 |
+
| (pl.col("LIBELLE_ACTE").str.contains(r"(?i)" + exp))
|
| 43 |
+
| (pl.col("LIBELLE_CARACTERISTIQUE").str.contains(r"(?i)" + exp))
|
| 44 |
+
| (pl.col("PARAGRAPHE").str.contains(r"(?i)" + exp))
|
| 45 |
+
)
|
| 46 |
+
.select("CODE_ACTE")
|
| 47 |
+
.unique()
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
df = (
|
| 51 |
+
acte.join(_temp, on="CODE_ACTE", how="inner")
|
| 52 |
+
.with_columns(
|
| 53 |
+
pl.lit(1).alias("flag"),
|
| 54 |
+
)
|
| 55 |
+
.pivot(
|
| 56 |
+
"LIBELLE_CARACTERISTIQUE",
|
| 57 |
+
index=["CODE_ACTE", "LIBELLE_ACTE", "PARAGRAPHE", "TECHNIQUE"],
|
| 58 |
+
values="flag",
|
| 59 |
+
sort_columns=True,
|
| 60 |
+
)
|
| 61 |
+
.sort("CODE_ACTE")
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
if selection:
|
| 65 |
+
_temp = acte.filter((pl.col("LIBELLE_CARACTERISTIQUE").is_in(selection))).select("CODE_ACTE").unique()
|
| 66 |
+
df = df.join(_temp, on="CODE_ACTE", how="inner").rename(names)
|
| 67 |
+
|
| 68 |
+
st.dataframe(df.to_pandas(), hide_index=True, use_container_width=True)
|
src/page/ctxt.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import unidecode
|
| 3 |
+
import polars as pl
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
GRIST_URL = "https://grist.numerique.gouv.fr/o/dim-chu-brest/api/docs/5ZXqTKBiHTD2qqUj6GVPtp" # ou ton serveur
|
| 8 |
+
TABLE = "Ctxt"
|
| 9 |
+
|
| 10 |
+
records = requests.get(f"{GRIST_URL}/tables/{TABLE}/records").json()["records"]
|
| 11 |
+
|
| 12 |
+
ctxt = pl.DataFrame(records).drop("id").unnest("fields").sort(pl.col("Code_CIM_10"))
|
| 13 |
+
|
| 14 |
+
st.title("Contexte patient")
|
| 15 |
+
|
| 16 |
+
st.dataframe(ctxt.to_pandas(), hide_index=True, use_container_width=True)
|
| 17 |
+
|
| 18 |
+
st.divider()
|
src/page/germes.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import unidecode
|
| 3 |
+
import polars as pl
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
GRIST_URL = "https://grist.numerique.gouv.fr/o/dim-chu-brest/api/docs/5ZXqTKBiHTD2qqUj6GVPtp" # ou ton serveur
|
| 8 |
+
TABLE = "Germes"
|
| 9 |
+
|
| 10 |
+
records = requests.get(f"{GRIST_URL}/tables/{TABLE}/records").json()["records"]
|
| 11 |
+
|
| 12 |
+
data = pl.DataFrame(records).drop("id").unnest("fields").select("Germe", "Code_du_germe", "Code_si_sepsis").sort("Germe")
|
| 13 |
+
|
| 14 |
+
st.title("Germes CIM-10")
|
| 15 |
+
|
| 16 |
+
st.divider()
|
| 17 |
+
|
| 18 |
+
# widgets
|
| 19 |
+
col1, col2, col3 = st.columns(3)
|
| 20 |
+
|
| 21 |
+
exp = col1.text_input("Mot(s) clef(s):", "")
|
| 22 |
+
exp = unidecode.unidecode(exp)
|
| 23 |
+
|
| 24 |
+
# filter data with options
|
| 25 |
+
df = data.filter(
|
| 26 |
+
(
|
| 27 |
+
(pl.col("Germe").str.contains(r"(?i)" + exp))
|
| 28 |
+
| (pl.col("Code_du_germe").str.contains(r"(?i)" + exp))
|
| 29 |
+
| (pl.col("Code_si_sepsis").str.contains(r"(?i)" + exp))
|
| 30 |
+
)
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
st.dataframe(
|
| 34 |
+
df.to_pandas(), hide_index=True, use_container_width=True
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
st.divider()
|
src/page/hello.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
st.header("Bienvenue sur notre application d'aide au codage PMSI+")
|
| 5 |
+
|
| 6 |
+
st.divider()
|
| 7 |
+
|
| 8 |
+
st.write(
|
| 9 |
+
"""Nous sommes ravis de vous accueillir dans cet outil conçu pour **faciliter votre travail** au sein de notre hôpital. Le Programme de Médicalisation des Systèmes d'Information (PMSI) est essentiel pour garantir une gestion optimale des données de santé, et nous savons que le codage peut parfois représenter un défi.
|
| 10 |
+
|
| 11 |
+
Notre application a été développée pour vous accompagner tout au long de ce processus.
|
| 12 |
+
|
| 13 |
+
Que vous soyez un professionnel de la santé expérimenté ou que vous débutiez dans ce domaine, nous sommes là pour vous soutenir.
|
| 14 |
+
Grâce à des _**ressources adaptées**_, des _**guides pratiques**_ et des _**outils de recherche avancés**_, vous pourrez **coder plus efficacement et avec confiance**.
|
| 15 |
+
|
| 16 |
+
N'hésitez pas à explorer toutes les fonctionnalités disponibles et à nous faire part de vos retours. Ensemble, nous pouvons **améliorer la qualité des données** et optimiser la prise en charge des patients.
|
| 17 |
+
|
| 18 |
+
**Merci de faire partie de cette aventure !**
|
| 19 |
+
|
| 20 |
+
_L’équipe de développement_"""
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
st.divider()
|
| 24 |
+
|
| 25 |
+
st.write("_Auteurs :_")
|
| 26 |
+
st.write("_**Département d'information médicale**_, CHU de Brest")
|
| 27 |
+
st.write("_**Centre de Données Cliniques**_, CHU de Brest")
|
src/page/intox.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import unidecode
|
| 3 |
+
import polars as pl
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
GRIST_URL = "https://grist.numerique.gouv.fr/o/dim-chu-brest/api/docs/5ZXqTKBiHTD2qqUj6GVPtp" # ou ton serveur
|
| 8 |
+
TABLE1 = "Intox_data"
|
| 9 |
+
TABLE2 = "Intox_data2"
|
| 10 |
+
|
| 11 |
+
records1 = requests.get(f"{GRIST_URL}/tables/{TABLE1}/records").json()["records"]
|
| 12 |
+
records2 = requests.get(f"{GRIST_URL}/tables/{TABLE2}/records").json()["records"]
|
| 13 |
+
|
| 14 |
+
cim10 = pl.DataFrame(records1).drop("id").unnest("fields").sort("SUBSTANCE")
|
| 15 |
+
data = pl.DataFrame(records2).drop("id").unnest("fields").sort("Nom")
|
| 16 |
+
|
| 17 |
+
data = data.with_columns(
|
| 18 |
+
pl.col("Nom")
|
| 19 |
+
.map_elements(lambda x: unidecode.unidecode(x), return_dtype=pl.Utf8)
|
| 20 |
+
.name.keep(),
|
| 21 |
+
pl.col("DCI")
|
| 22 |
+
.map_elements(lambda x: unidecode.unidecode(x), return_dtype=pl.Utf8)
|
| 23 |
+
.name.keep(),
|
| 24 |
+
pl.col("SUBSTANCE")
|
| 25 |
+
.map_elements(lambda x: unidecode.unidecode(x), return_dtype=pl.Utf8)
|
| 26 |
+
.name.keep(),
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
st.title("Intox CIM-10")
|
| 30 |
+
|
| 31 |
+
st.divider()
|
| 32 |
+
|
| 33 |
+
st.header("Médicaments - Codes CIM-10")
|
| 34 |
+
|
| 35 |
+
# widgets
|
| 36 |
+
col1, col2, col3 = st.columns(3)
|
| 37 |
+
|
| 38 |
+
exp = col1.text_input("Mot(s) clef(s):", "")
|
| 39 |
+
exp = unidecode.unidecode(exp)
|
| 40 |
+
fm = col3.toggle("Afficher les correspondances exactes", value=True)
|
| 41 |
+
|
| 42 |
+
# filter data with options
|
| 43 |
+
df = data.filter(
|
| 44 |
+
(
|
| 45 |
+
(pl.col("Nom").str.contains("(?i)" + exp))
|
| 46 |
+
| (pl.col("DCI").str.contains("(?i)" + exp))
|
| 47 |
+
| (pl.col("SUBSTANCE").str.contains("(?i)" + exp))
|
| 48 |
+
)
|
| 49 |
+
& ((pl.col("full_match") == fm) | (pl.col("full_match") == True))
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
st.dataframe(
|
| 53 |
+
df.drop("full_match").to_pandas(), hide_index=True, use_container_width=True
|
| 54 |
+
)
|
| 55 |
+
st.divider()
|
| 56 |
+
|
| 57 |
+
st.header("Table des effets nocifs")
|
| 58 |
+
|
| 59 |
+
# widgets
|
| 60 |
+
col4, col5, col6 = st.columns(3)
|
| 61 |
+
|
| 62 |
+
exp2 = col4.text_input("Mot(s) clef(s): ", "")
|
| 63 |
+
exp2 = unidecode.unidecode(exp2)
|
| 64 |
+
|
| 65 |
+
# filter data with options
|
| 66 |
+
df2 = cim10.filter(pl.col("SUBSTANCE").str.contains("(?i)" + exp2))
|
| 67 |
+
|
| 68 |
+
st.dataframe(df2.to_pandas(), hide_index=True, use_container_width=True)
|
| 69 |
+
st.divider()
|
src/streamlit_app.py
CHANGED
|
@@ -1,40 +1,20 @@
|
|
| 1 |
-
import altair as alt
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
import streamlit as st
|
| 5 |
|
| 6 |
-
"""
|
| 7 |
-
# Welcome to Streamlit!
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
indices = np.linspace(0, 1, num_points)
|
| 20 |
-
theta = 2 * np.pi * num_turns * indices
|
| 21 |
-
radius = indices
|
| 22 |
-
|
| 23 |
-
x = radius * np.cos(theta)
|
| 24 |
-
y = radius * np.sin(theta)
|
| 25 |
-
|
| 26 |
-
df = pd.DataFrame({
|
| 27 |
-
"x": x,
|
| 28 |
-
"y": y,
|
| 29 |
-
"idx": indices,
|
| 30 |
-
"rand": np.random.randn(num_points),
|
| 31 |
-
})
|
| 32 |
-
|
| 33 |
-
st.altair_chart(alt.Chart(df, height=700, width=700)
|
| 34 |
-
.mark_point(filled=True)
|
| 35 |
-
.encode(
|
| 36 |
-
x=alt.X("x", axis=None),
|
| 37 |
-
y=alt.Y("y", axis=None),
|
| 38 |
-
color=alt.Color("idx", legend=None, scale=alt.Scale()),
|
| 39 |
-
size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
|
| 40 |
-
))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
|
|
|
|
|
|
|
| 3 |
|
| 4 |
+
hello_page = st.Page("page/hello.py", title="Bienvenue !", icon="👋")
|
| 5 |
+
intox_page = st.Page("page/intox.py", title="Intox CIM-10", icon="☠️")
|
| 6 |
+
ctxt_page = st.Page("page/ctxt.py", title="Contexte patient", icon="😷")
|
| 7 |
+
acte_page = st.Page("page/acte.py", title="Actes CCAM", icon="🩺")
|
| 8 |
+
germes_page = st.Page("page/germes.py", title="Germes CIM-10", icon="🦠")
|
| 9 |
|
| 10 |
+
pg = st.navigation(
|
| 11 |
+
{
|
| 12 |
+
"PMSI+": [hello_page],
|
| 13 |
+
"CIM-10": [intox_page, germes_page],
|
| 14 |
+
"CCAM": [],
|
| 15 |
+
"HDJ": [acte_page, ctxt_page],
|
| 16 |
+
}
|
| 17 |
+
)
|
| 18 |
|
| 19 |
+
st.set_page_config(page_title="PMSI+", page_icon="📚", layout="wide")
|
| 20 |
+
pg.run()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|