Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -123,30 +123,104 @@ with tab_intelligence:
|
|
| 123 |
if not json_files:
|
| 124 |
st.info("Aucun document analysé disponible. Allez dans l'onglet INGESTION.")
|
| 125 |
else:
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
st.
|
| 132 |
-
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
| 134 |
|
|
|
|
| 135 |
with open(selected_file, 'r', encoding='utf-8') as f:
|
| 136 |
data = json.load(f)
|
|
|
|
| 137 |
text_extracted = " ".join([t.get("text", "") for t in data.get("texts", [])])
|
| 138 |
|
|
|
|
|
|
|
| 139 |
with col_inf1:
|
| 140 |
-
st.markdown("###
|
| 141 |
-
st.text_area("Données OCR", text_extracted, height=
|
| 142 |
|
| 143 |
with col_inf2:
|
| 144 |
-
st.markdown("###
|
| 145 |
-
|
| 146 |
-
|
|
|
|
| 147 |
graph_data = st.session_state.extractor.extract_graph(text_extracted)
|
|
|
|
| 148 |
if graph_data:
|
| 149 |
st.session_state.last_graph = graph_data
|
| 150 |
-
st.
|
| 151 |
else:
|
| 152 |
-
st.error("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
if not json_files:
|
| 124 |
st.info("Aucun document analysé disponible. Allez dans l'onglet INGESTION.")
|
| 125 |
else:
|
| 126 |
+
# Barre d'outils supérieure pour l'artefact
|
| 127 |
+
col_select, col_delete = st.columns([3, 1])
|
| 128 |
+
with col_select:
|
| 129 |
+
selected_file = st.selectbox("Sélectionner un artefact", json_files, format_func=lambda x: x.name)
|
| 130 |
+
with col_delete:
|
| 131 |
+
st.write("") # Espacement
|
| 132 |
+
if st.button("🗑️ SUPPRIMER", use_container_width=True):
|
| 133 |
+
os.remove(selected_file)
|
| 134 |
+
st.rerun()
|
| 135 |
+
|
| 136 |
+
st.markdown("---")
|
| 137 |
|
| 138 |
+
# Lecture des données
|
| 139 |
with open(selected_file, 'r', encoding='utf-8') as f:
|
| 140 |
data = json.load(f)
|
| 141 |
+
# Récupération du texte structuré (Docling export_to_dict format)
|
| 142 |
text_extracted = " ".join([t.get("text", "") for t in data.get("texts", [])])
|
| 143 |
|
| 144 |
+
col_inf1, col_inf2 = st.columns([1, 2]) # On donne plus de place au graphe
|
| 145 |
+
|
| 146 |
with col_inf1:
|
| 147 |
+
st.markdown("### 📄 TEXTE SOURCE")
|
| 148 |
+
st.text_area("Données issues de l'OCR", text_extracted, height=500)
|
| 149 |
|
| 150 |
with col_inf2:
|
| 151 |
+
st.markdown("### 🕸️ GRAPHE DE CONNAISSANCE")
|
| 152 |
+
|
| 153 |
+
if st.button("🧬 GÉNÉRER L'INTELLIGENCE VISUELLE", use_container_width=True):
|
| 154 |
+
with st.spinner("Le cerveau Qwen extrait les relations systémiques..."):
|
| 155 |
graph_data = st.session_state.extractor.extract_graph(text_extracted)
|
| 156 |
+
|
| 157 |
if graph_data:
|
| 158 |
st.session_state.last_graph = graph_data
|
| 159 |
+
st.success(f"Analyse terminée : {len(graph_data.get('entities', []))} entités identifiées.")
|
| 160 |
else:
|
| 161 |
+
st.error("L'IA n'a pas pu structurer les données. Vérifiez la qualité du texte.")
|
| 162 |
+
|
| 163 |
+
# Affichage du Graphe si disponible
|
| 164 |
+
if 'last_graph' in st.session_state and st.session_state.last_graph:
|
| 165 |
+
# Configuration PyVis
|
| 166 |
+
net = Network(
|
| 167 |
+
height="500px",
|
| 168 |
+
width="100%",
|
| 169 |
+
bgcolor="#0b0d11",
|
| 170 |
+
font_color="#e6edf3",
|
| 171 |
+
directed=True
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
# Couleurs par type d'entité (Palette Gotham/Cyber)
|
| 175 |
+
colors = {
|
| 176 |
+
"Person": "#29b5e8", # Bleu brillant
|
| 177 |
+
"Organization": "#ffcc00", # Jaune
|
| 178 |
+
"Location": "#00ffcc", # Turquoise
|
| 179 |
+
"Concept": "#9966ff", # Violet
|
| 180 |
+
"Event": "#ff6666" # Rouge doux
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
# Ajout des Noeuds
|
| 184 |
+
for ent in st.session_state.last_graph.get("entities", []):
|
| 185 |
+
color = colors.get(ent.get("type"), "#808080")
|
| 186 |
+
net.add_node(
|
| 187 |
+
ent["id"],
|
| 188 |
+
label=ent["name"],
|
| 189 |
+
title=f"{ent['type']}: {ent['description']}",
|
| 190 |
+
color=color,
|
| 191 |
+
shape="dot",
|
| 192 |
+
size=20
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
# Ajout des Relations
|
| 196 |
+
for rel in st.session_state.last_graph.get("relationships", []):
|
| 197 |
+
net.add_edge(
|
| 198 |
+
rel["from"],
|
| 199 |
+
rel["to"],
|
| 200 |
+
label=rel["type"],
|
| 201 |
+
color="#30363d",
|
| 202 |
+
arrows="to"
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
# Options physiques pour un mouvement fluide
|
| 206 |
+
net.set_options("""
|
| 207 |
+
var options = {
|
| 208 |
+
"physics": {
|
| 209 |
+
"forceAtlas2Based": { "gravitationalConstant": -50, "centralGravity": 0.01, "springLength": 100 },
|
| 210 |
+
"maxVelocity": 50,
|
| 211 |
+
"solver": "forceAtlas2Based",
|
| 212 |
+
"timestep": 0.35
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
""")
|
| 216 |
+
|
| 217 |
+
# Rendu du graphe dans Streamlit
|
| 218 |
+
try:
|
| 219 |
+
path = "graph_temp.html"
|
| 220 |
+
net.save_graph(path)
|
| 221 |
+
with open(path, 'r', encoding='utf-8') as f:
|
| 222 |
+
html_content = f.read()
|
| 223 |
+
st.components.v1.html(html_content, height=550)
|
| 224 |
+
except Exception as e:
|
| 225 |
+
st.error(f"Erreur de rendu visuel : {e}")
|
| 226 |
+
st.json(st.session_state.last_graph) # Fallback JSON si PyVis échoue
|