Finalisation de la persistance

This commit is contained in:
Stéphan Peccini 2025-06-11 17:05:05 +02:00
parent 8efc016014
commit 67182d8b53
6 changed files with 241 additions and 67 deletions

View File

@ -230,7 +230,7 @@ def configurer_filtres_vulnerabilite() -> Tuple[bool, bool, bool, str, bool, str
init_checkbox("analyse_filtrer_ihh", "pages.analyse.filter_ihh") init_checkbox("analyse_filtrer_ihh", "pages.analyse.filter_ihh")
init_checkbox("analyse_filtrer_isg", "pages.analyse.filter_isg") init_checkbox("analyse_filtrer_isg", "pages.analyse.filter_isg")
init_radio("analyse_ihh_type", "pages.analyse.apply_ihh_filter", ["Pays", "Acteurs"], "Pays") init_radio("analyse_ihh_type", "pages.analyse.apply_ihh_filter", ["Pays", "Acteurs"], "Pays")
init_radio("analyse_logique_filtrage", "pages.analyse.filter_logic", ["ou", "et"], "ou") init_radio("analyse_logique_filtrage", "pages.analyse.filter_logic", ["OU", "ET"], "OU")
filtrer_ics = st.checkbox(str(_("pages.analyse.filter_ics")), key="analyse_filtrer_ics") filtrer_ics = st.checkbox(str(_("pages.analyse.filter_ics")), key="analyse_filtrer_ics")
filtrer_ivc = st.checkbox(str(_("pages.analyse.filter_ivc")), key="analyse_filtrer_ivc") filtrer_ivc = st.checkbox(str(_("pages.analyse.filter_ivc")), key="analyse_filtrer_ivc")
@ -247,10 +247,10 @@ def configurer_filtres_vulnerabilite() -> Tuple[bool, bool, bool, str, bool, str
filtrer_isg = st.checkbox(str(_("pages.analyse.filter_isg")), key="analyse_filtrer_isg") filtrer_isg = st.checkbox(str(_("pages.analyse.filter_isg")), key="analyse_filtrer_isg")
logique_options = ["ou", "et"] logique_options = ["OU", "ET"]
logique_labels = { logique_labels = {
"ou": str(_("pages.analyse.or")), "OU": str(_("pages.analyse.or")),
"et": str(_("pages.analyse.and")) "ET": str(_("pages.analyse.and"))
} }
logique_filtrage = st.radio( logique_filtrage = st.radio(

View File

@ -18,22 +18,6 @@ from app.fiches.utils import (
from app.fiches.generer import generer_fiche from app.fiches.generer import generer_fiche
def interface_fiches() -> None: def interface_fiches() -> None:
"""
Affiche l'interface utilisateur des fiches.
Parameters
----------
Aucun
Notes
-----
Cette fonction initialise l'interface utilisateur qui permet aux utilisateurs d'afficher,
visualiser et interagir avec les fiches. Elle gère également :
- Le chargement de l'arborescence des fiches depuis Gitea.
- La navigation entre différentes catégories de fiches.
- L'affichage des tickets associés aux fiches.
- Le téléchargement des PDF (si disponible).
"""
st.markdown(f"# {str(_('pages.fiches.title'))}") st.markdown(f"# {str(_('pages.fiches.title'))}")
html_expander(f"{str(_('pages.fiches.help'))}", content="\n".join(_("pages.fiches.help_content")), open_by_default=False, details_class="details_introduction") html_expander(f"{str(_('pages.fiches.help'))}", content="\n".join(_("pages.fiches.help_content")), open_by_default=False, details_class="details_introduction")
st.markdown("---") st.markdown("---")
@ -47,19 +31,45 @@ def interface_fiches() -> None:
return return
dossiers = sorted(arbo.keys(), key=lambda x: x.lower()) dossiers = sorted(arbo.keys(), key=lambda x: x.lower())
dossier_choisi = st.selectbox(
str(_("pages.fiches.choose_category",)), if "dossier_choisi" not in st.session_state or st.session_state["dossier_choisi"] not in dossiers:
[str(_("pages.fiches.select_folder"))] + dossiers st.session_state["dossier_choisi"] = str(_("pages.fiches.select_folder"))
try:
index_dossier = [str(_("pages.fiches.select_folder"))] + dossiers
idx = index_dossier.index(st.session_state["dossier_choisi"])
except ValueError:
idx = 0
st.session_state["dossier_choisi"] = st.selectbox(
str(_("pages.fiches.choose_category")),
[str(_("pages.fiches.select_folder"))] + dossiers,
index=idx
) )
dossier_choisi = st.session_state["dossier_choisi"]
if dossier_choisi and dossier_choisi != str(_("pages.fiches.select_folder")): if dossier_choisi and dossier_choisi != str(_("pages.fiches.select_folder")):
fiches = arbo.get(dossier_choisi, []) fiches = arbo.get(dossier_choisi, [])
noms_fiches = [f['nom'] for f in fiches] noms_fiches = [f['nom'] for f in fiches]
fiche_choisie = st.selectbox(
if "fiche_choisie" not in st.session_state or st.session_state["fiche_choisie"] not in noms_fiches:
st.session_state["fiche_choisie"] = str(_("pages.fiches.select_file"))
try:
index_fiche = [str(_("pages.fiches.select_file"))] + noms_fiches
idx_fiche = index_fiche.index(st.session_state["fiche_choisie"])
except ValueError:
idx_fiche = 0
st.session_state["fiche_choisie"] = st.selectbox(
str(_("pages.fiches.choose_file")), str(_("pages.fiches.choose_file")),
[str(_("pages.fiches.select_file"))] + noms_fiches [str(_("pages.fiches.select_file"))] + noms_fiches,
index=idx_fiche
) )
fiche_choisie = st.session_state["fiche_choisie"]
if fiche_choisie and fiche_choisie != str(_("pages.fiches.select_file")): if fiche_choisie and fiche_choisie != str(_("pages.fiches.select_file")):
fiche_info = next((f for f in fiches if f["nom"] == fiche_choisie), None) fiche_info = next((f for f in fiches if f["nom"] == fiche_choisie), None)
if fiche_info: if fiche_info:
@ -99,7 +109,6 @@ def interface_fiches() -> None:
pdf_name = nom_fiche + ".pdf" pdf_name = nom_fiche + ".pdf"
pdf_path = os.path.join("static", "Fiches", dossier_choisi, pdf_name) pdf_path = os.path.join("static", "Fiches", dossier_choisi, pdf_name)
# Bouton de téléchargement du PDF
if os.path.exists(pdf_path): if os.path.exists(pdf_path):
with open(pdf_path, "rb") as pdf_file: with open(pdf_path, "rb") as pdf_file:
st.download_button( st.download_button(

View File

@ -1,30 +1,19 @@
# === Ajout de produit personnalisé ===
import streamlit as st import streamlit as st
import networkx as nx import networkx as nx
from utils.translations import _ from utils.translations import _
from utils.persistance import maj_champ_statut, get_champ_statut from utils.persistance import get_champ_statut, maj_champ_statut, supprime_champ_statut
def ajouter_produit(G: nx.DiGraph) -> nx.DiGraph: def ajouter_produit(G: nx.DiGraph) -> nx.DiGraph:
"""
Ajoute un produit personnalisé dans le graphe en cours.
Args:
G (nx.DiGraph): graphe en cours.
Returns:
nx.DiGraph: le graphe avec le nouveau produit
Notes:
Cette fonction ajoute un nouveau produit final temporaire
au graphe de référence.
"""
st.markdown(f"## {str(_('pages.personnalisation.add_new_product'))}") st.markdown(f"## {str(_('pages.personnalisation.add_new_product'))}")
# Restauration des produits personnalisés sauvegardés
if "pages.personnalisation.create_product.fait" not in st.session_state and get_champ_statut("pages.personnalisation.create_product.fait") == "oui": if "pages.personnalisation.create_product.fait" not in st.session_state and get_champ_statut("pages.personnalisation.create_product.fait") == "oui":
index = 0 index = 0
while True: while True:
new_prod = get_champ_statut(f"pages.personnalisation.create_product.{index}.nom") new_prod = get_champ_statut(f"pages.personnalisation.create_product.{index}.nom")
if new_prod == "": if new_prod == "":
break # Fin de la liste break
G.add_node(new_prod, niveau="0", personnalisation="oui", label=new_prod) G.add_node(new_prod, niveau="0", personnalisation="oui", label=new_prod)
@ -42,35 +31,77 @@ def ajouter_produit(G: nx.DiGraph) -> nx.DiGraph:
index += 1 index += 1
new_prod = st.text_input(str(_("pages.personnalisation.new_product_name")), key="new_prod") niveau1 = sorted([n for n, d in G.nodes(data=True) if d.get("niveau") == "1"])
ops_dispo = sorted([n for n, d in G.nodes(data=True) if d.get("niveau") == "assemblage"])
if "pages.personnalisation.add.new_product_name" not in st.session_state:
st.session_state["pages.personnalisation.add.new_product_name"] = get_champ_statut("pages.personnalisation.add.new_product_name")
new_prod = st.text_input(
str(_("pages.personnalisation.new_product_name")),
key="pages.personnalisation.add.new_product_name"
)
if new_prod: if new_prod:
if new_prod in G.nodes:
st.warning(str(_("pages.personnalisation.product_exists")))
return G
if "pages.personnalisation.add.assembly_operation" not in st.session_state:
st.session_state["pages.personnalisation.add.assembly_operation"] = get_champ_statut("pages.personnalisation.add.assembly_operation")
if "pages.personnalisation.add.components_to_link" not in st.session_state:
composants = []
i = 0
while True:
val = get_champ_statut(f"pages.personnalisation.add.components_to_link.{i}")
if val == "":
break
composants.append(val)
i += 1
st.session_state["pages.personnalisation.add.components_to_link"] = composants
ops_dispo = sorted([ ops_dispo = sorted([
n for n, d in G.nodes(data=True) n for n, d in G.nodes(data=True)
if d.get("niveau") == "10" if d.get("niveau") == "10"
and any(G.has_edge(p, n) and G.nodes[p].get("niveau") == "0" for p in G.predecessors(n)) and any(G.has_edge(p, n) and G.nodes[p].get("niveau") == "0" for p in G.predecessors(n))
]) ])
sel_new_op = st.selectbox(str(_("pages.personnalisation.assembly_operation")), [str(_("pages.personnalisation.none"))] + ops_dispo, index=0) sel_new_op = st.selectbox(str(_("pages.personnalisation.assembly_operation")), ops_dispo,
key="pages.personnalisation.add.assembly_operation")
niveau1 = sorted([n for n, d in G.nodes(data=True) if d.get("niveau") == "1"]) sel_comps = st.multiselect(
sel_comps = st.multiselect(str(_("pages.personnalisation.components_to_link")), options=niveau1) str(_("pages.personnalisation.components_to_link")),
options=niveau1,
key="pages.personnalisation.add.components_to_link"
)
if st.button(str(_("pages.personnalisation.create_product"))): if st.button(str(_("pages.personnalisation.create_product"))):
G.add_node(new_prod, niveau="0", personnalisation="oui", label=new_prod) G.add_node(new_prod, niveau="0", personnalisation="oui", label=new_prod)
maj_champ_statut("pages.personnalisation.create_product.fait", "oui")
st.session_state["pages.personnalisation.create_product.fait"] = "oui"
i = 0 # Trouver le prochain index disponible
while get_champ_statut(f"pages.personnalisation.create_product.{i}.nom") != "": index = 0
i += 1 while get_champ_statut(f"pages.personnalisation.create_product.{index}.nom") != "":
maj_champ_statut(f"pages.personnalisation.create_product.{i}.nom", new_prod) index += 1
maj_champ_statut(f"pages.personnalisation.create_product.{index}.nom", new_prod)
if sel_new_op != str(_("pages.personnalisation.none")): if sel_new_op != str(_("pages.personnalisation.none")):
G.add_edge(new_prod, sel_new_op) G.add_edge(new_prod, sel_new_op)
maj_champ_statut(f"pages.personnalisation.create_product.{i}.edge", sel_new_op) maj_champ_statut(f"pages.personnalisation.create_product.{index}.edge", sel_new_op)
for j, comp in enumerate(sel_comps): for j, comp in enumerate(sel_comps):
G.add_edge(new_prod, comp) G.add_edge(new_prod, comp)
maj_champ_statut(f"pages.personnalisation.create_product.{i}.composants.{j}", comp) maj_champ_statut(f"pages.personnalisation.create_product.{index}.composants.{j}", comp)
maj_champ_statut("pages.personnalisation.create_product.fait", "oui")
# Nettoyage de session et des champs persistants
del st.session_state["pages.personnalisation.add.new_product_name"]
del st.session_state["pages.personnalisation.add.assembly_operation"]
del st.session_state["pages.personnalisation.add.components_to_link"]
supprime_champ_statut("pages.personnalisation.add")
st.success(f"{new_prod} {str(_('pages.personnalisation.added'))}") st.success(f"{new_prod} {str(_('pages.personnalisation.added'))}")
st.rerun()
return G return G

View File

@ -32,6 +32,7 @@ def supprimer_produit(
G.remove_node(prod) G.remove_node(prod)
st.success(f"{prod} {str(_('pages.personnalisation.deleted'))}") st.success(f"{prod} {str(_('pages.personnalisation.deleted'))}")
st.session_state.pop("prod_sel", None) st.session_state.pop("prod_sel", None)
st.rerun()
return G return G
def get_operations_disponibles( def get_operations_disponibles(
@ -171,47 +172,178 @@ def modifier_produit(
""" """
st.markdown(f"## {str(_('pages.personnalisation.modify_product'))}") st.markdown(f"## {str(_('pages.personnalisation.modify_product'))}")
# Restauration des produits personnalisés sauvegardés
if "pages.personnalisation.create_product.fait" not in st.session_state and get_champ_statut("pages.personnalisation.create_product.fait") == "oui":
index = 0
while True:
new_prod = get_champ_statut(f"pages.personnalisation.create_product.{index}.nom")
if new_prod == "":
break
G.add_node(new_prod, niveau="0", personnalisation="oui", label=new_prod)
sel_new_op = get_champ_statut(f"pages.personnalisation.create_product.{index}.edge")
if sel_new_op:
G.add_edge(new_prod, sel_new_op)
i = 0
while True:
comp = get_champ_statut(f"pages.personnalisation.create_product.{index}.composants.{i}")
if comp == "":
break
G.add_edge(new_prod, comp)
i += 1
index += 1
st.session_state["pages.personnalisation.create_product.fait"] = "oui"
# Sélection du produit à modifier # Sélection du produit à modifier
produits0 = get_produits_personnalises(G) produits0 = get_produits_personnalises(G)
sel_display = st.selectbox(label=str(_("pages.personnalisation.products_to_modify")), options=produits0)
# Restaurer la sélection du produit depuis la persistance
if "pages.personnalisation.modify.selected_product" not in st.session_state:
st.session_state["pages.personnalisation.modify.selected_product"] = get_champ_statut("pages.personnalisation.modify.selected_product")
sel_display = st.selectbox(
label=str(_("pages.personnalisation.products_to_modify")),
options=produits0,
key="pages.personnalisation.modify.selected_product"
)
if sel_display:
maj_champ_statut("pages.personnalisation.modify.selected_product", sel_display)
if not sel_display: if not sel_display:
return G return G
# Obtention du produit sélectionné # Obtention du produit sélectionné
prod = sel_display prod = sel_display
# Trouver l'index du produit dans la sauvegarde
index_key = None
index = 0 index = 0
while get_champ_statut(f"pages.personnalisation.create_product.{index}.nom") != sel_display: while True:
saved_name = get_champ_statut(f"pages.personnalisation.create_product.{index}.nom")
if saved_name == "":
break
if saved_name == prod:
index_key = index
break
index += 1 index += 1
# Suppression du produit si demandé # Suppression du produit si demandé
if st.button(f"{str(_('pages.personnalisation.delete'))} {prod}"): if st.button(f"{str(_('pages.personnalisation.delete'))} {prod}"):
supprime_champ_statut(f"pages.personnalisation.create_product.{index}") if index_key is not None:
if index == 0: # Supprimer les données du produit
supprime_champ_statut("pages.personnalisation.create_product") supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.nom")
supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.edge")
supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants")
# Réorganiser les indices pour combler le trou
next_index = index_key + 1
while get_champ_statut(f"pages.personnalisation.create_product.{next_index}.nom") != "":
# Déplacer le produit suivant vers l'index actuel
nom = get_champ_statut(f"pages.personnalisation.create_product.{next_index}.nom")
edge = get_champ_statut(f"pages.personnalisation.create_product.{next_index}.edge")
maj_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.nom", nom)
if edge:
maj_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.edge", edge)
else:
supprime_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.edge")
# Copier les composants
i = 0
while True:
comp = get_champ_statut(f"pages.personnalisation.create_product.{next_index}.composants.{i}")
if comp == "":
break
maj_champ_statut(f"pages.personnalisation.create_product.{next_index - 1}.composants.{i}", comp)
i += 1
# Supprimer l'ancien emplacement
supprime_champ_statut(f"pages.personnalisation.create_product.{next_index}")
next_index += 1
# Nettoyer aussi les champs de modification
supprime_champ_statut("pages.personnalisation.modify")
if "pages.personnalisation.modify.selected_product" in st.session_state:
del st.session_state["pages.personnalisation.modify.selected_product"]
if "pages.personnalisation.modify.selected_operation" in st.session_state:
del st.session_state["pages.personnalisation.modify.selected_operation"]
if "pages.personnalisation.modify.selected_components" in st.session_state:
del st.session_state["pages.personnalisation.modify.selected_components"]
return supprimer_produit(G, prod) return supprimer_produit(G, prod)
# Gestion des opérations d'assemblage # Gestion des opérations d'assemblage
ops_dispo = get_operations_disponibles(G) ops_dispo = get_operations_disponibles(G)
curr_ops = get_operations_actuelles(G, prod) curr_ops = get_operations_actuelles(G, prod)
default_idx = ops_dispo.index(curr_ops[0]) + 1 if curr_ops and curr_ops[0] in ops_dispo else 0
sel_op = st.selectbox(str(_("pages.personnalisation.linked_assembly_operation")), [str(_("pages.personnalisation.none"))] + ops_dispo, index=default_idx) # Restaurer la sélection de l'opération depuis la persistance
if "pages.personnalisation.modify.selected_operation" not in st.session_state:
saved_op = get_champ_statut("pages.personnalisation.modify.selected_operation")
if saved_op and saved_op in [str(_("pages.personnalisation.none"))] + ops_dispo:
st.session_state["pages.personnalisation.modify.selected_operation"] = saved_op
else:
default_op = curr_ops[0] if curr_ops else str(_("pages.personnalisation.none"))
st.session_state["pages.personnalisation.modify.selected_operation"] = default_op
sel_op = st.selectbox(
str(_("pages.personnalisation.linked_assembly_operation")),
[str(_("pages.personnalisation.none"))] + ops_dispo,
key="pages.personnalisation.modify.selected_operation"
)
if sel_op:
maj_champ_statut("pages.personnalisation.modify.selected_operation", sel_op)
# Gestion des composants # Gestion des composants
niveau1 = get_composants_niveau1(G) niveau1 = get_composants_niveau1(G)
linked = get_composants_lies(G, prod) linked = get_composants_lies(G, prod)
nouveaux = st.multiselect(f"{str(_('pages.personnalisation.components_linked_to'))} {prod}", options=niveau1, default=linked)
# Restaurer la sélection des composants depuis la persistance
if "pages.personnalisation.modify.selected_components" not in st.session_state:
saved_comps = []
i = 0
while True:
comp = get_champ_statut(f"pages.personnalisation.modify.selected_components.{i}")
if comp == "":
break
if comp in niveau1:
saved_comps.append(comp)
i += 1
st.session_state["pages.personnalisation.modify.selected_components"] = saved_comps if saved_comps else linked
nouveaux = st.multiselect(
f"{str(_('pages.personnalisation.components_linked_to'))} {prod}",
options=niveau1,
key="pages.personnalisation.modify.selected_components"
)
if nouveaux:
supprime_champ_statut("pages.personnalisation.modify.selected_components")
for i, comp in enumerate(nouveaux):
maj_champ_statut(f"pages.personnalisation.modify.selected_components.{i}", comp)
# Mise à jour des liens si demandé # Mise à jour des liens si demandé
if st.button(f"{str(_('pages.personnalisation.update'))} {prod}"): if st.button(f"{str(_('pages.personnalisation.update'))} {prod}"):
G = mettre_a_jour_operations(G, prod, curr_ops, sel_op) G = mettre_a_jour_operations(G, prod, curr_ops, sel_op)
if sel_op: if index_key is not None:
maj_champ_statut(f"pages.personnalisation.create_product.{index}.edge", sel_op) if sel_op != str(_("pages.personnalisation.none")):
maj_champ_statut(f"pages.personnalisation.create_product.{index_key}.edge", sel_op)
else:
supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.edge")
G = mettre_a_jour_composants(G, prod, linked, nouveaux) G = mettre_a_jour_composants(G, prod, linked, nouveaux)
if index_key is not None:
if nouveaux: if nouveaux:
supprime_champ_statut(f"pages.personnalisation.create_product.{index}.composants") supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants")
for j, comp in enumerate(nouveaux): for j, comp in enumerate(nouveaux):
maj_champ_statut(f"pages.personnalisation.create_product.{index}.composants.{j}", comp) maj_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants.{j}", comp)
else:
supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants")
st.success(f"{prod} {str(_('pages.personnalisation.updated'))}") st.success(f"{prod} {str(_('pages.personnalisation.updated'))}")
# Forcer la sauvegarde de l'état
maj_champ_statut("pages.personnalisation.create_product.fait", "oui")
return G return G

View File

@ -66,6 +66,7 @@
"add_new_product": "Add a new final product", "add_new_product": "Add a new final product",
"new_product_name": "New product name (unique)", "new_product_name": "New product name (unique)",
"assembly_operation": "Assembly operation (optional)", "assembly_operation": "Assembly operation (optional)",
"product_exists": "This product already exists",
"none": "-- None --", "none": "-- None --",
"components_to_link": "Components to link", "components_to_link": "Components to link",
"create_product": "Create product", "create_product": "Create product",

View File

@ -66,6 +66,7 @@
"add_new_product": "Ajouter un nouveau produit final", "add_new_product": "Ajouter un nouveau produit final",
"new_product_name": "Nom du nouveau produit (unique)", "new_product_name": "Nom du nouveau produit (unique)",
"assembly_operation": "Opération d'assemblage (optionnelle)", "assembly_operation": "Opération d'assemblage (optionnelle)",
"product_exists": "Ce produit existe déjà.",
"none": "-- Aucune --", "none": "-- Aucune --",
"components_to_link": "Composants à lier", "components_to_link": "Composants à lier",
"create_product": "Créer le produit", "create_product": "Créer le produit",