diff --git a/app/analyse/interface.py b/app/analyse/interface.py index 0de2163..e3c10ea 100644 --- a/app/analyse/interface.py +++ b/app/analyse/interface.py @@ -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_isg", "pages.analyse.filter_isg") 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_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") - logique_options = ["ou", "et"] + logique_options = ["OU", "ET"] logique_labels = { - "ou": str(_("pages.analyse.or")), - "et": str(_("pages.analyse.and")) + "OU": str(_("pages.analyse.or")), + "ET": str(_("pages.analyse.and")) } logique_filtrage = st.radio( diff --git a/app/fiches/interface.py b/app/fiches/interface.py index 96dabab..6566ac4 100644 --- a/app/fiches/interface.py +++ b/app/fiches/interface.py @@ -18,22 +18,6 @@ from app.fiches.utils import ( from app.fiches.generer import generer_fiche 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'))}") html_expander(f"{str(_('pages.fiches.help'))}", content="\n".join(_("pages.fiches.help_content")), open_by_default=False, details_class="details_introduction") st.markdown("---") @@ -47,19 +31,45 @@ def interface_fiches() -> None: return dossiers = sorted(arbo.keys(), key=lambda x: x.lower()) - dossier_choisi = st.selectbox( - str(_("pages.fiches.choose_category",)), - [str(_("pages.fiches.select_folder"))] + dossiers + + if "dossier_choisi" not in st.session_state or st.session_state["dossier_choisi"] not in 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")): fiches = arbo.get(dossier_choisi, []) 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.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")): fiche_info = next((f for f in fiches if f["nom"] == fiche_choisie), None) if fiche_info: @@ -99,7 +109,6 @@ def interface_fiches() -> None: pdf_name = nom_fiche + ".pdf" pdf_path = os.path.join("static", "Fiches", dossier_choisi, pdf_name) - # Bouton de téléchargement du PDF if os.path.exists(pdf_path): with open(pdf_path, "rb") as pdf_file: st.download_button( diff --git a/app/personnalisation/utils/ajout.py b/app/personnalisation/utils/ajout.py index 1f2f5af..a32b281 100644 --- a/app/personnalisation/utils/ajout.py +++ b/app/personnalisation/utils/ajout.py @@ -1,30 +1,19 @@ +# === Ajout de produit personnalisé === import streamlit as st import networkx as nx 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: - """ - 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'))}") + # 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 # Fin de la liste + break 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 - 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 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([ n for n, d in G.nodes(data=True) 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)) ]) - 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(str(_("pages.personnalisation.components_to_link")), options=niveau1) + sel_comps = st.multiselect( + str(_("pages.personnalisation.components_to_link")), + options=niveau1, + key="pages.personnalisation.add.components_to_link" + ) if st.button(str(_("pages.personnalisation.create_product"))): 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 - while get_champ_statut(f"pages.personnalisation.create_product.{i}.nom") != "": - i += 1 - maj_champ_statut(f"pages.personnalisation.create_product.{i}.nom", new_prod) + # Trouver le prochain index disponible + index = 0 + while get_champ_statut(f"pages.personnalisation.create_product.{index}.nom") != "": + index += 1 + + maj_champ_statut(f"pages.personnalisation.create_product.{index}.nom", new_prod) if sel_new_op != str(_("pages.personnalisation.none")): 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): 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.rerun() + return G diff --git a/app/personnalisation/utils/modification.py b/app/personnalisation/utils/modification.py index f4ebf47..0b1c5da 100644 --- a/app/personnalisation/utils/modification.py +++ b/app/personnalisation/utils/modification.py @@ -32,6 +32,7 @@ def supprimer_produit( G.remove_node(prod) st.success(f"{prod} {str(_('pages.personnalisation.deleted'))}") st.session_state.pop("prod_sel", None) + st.rerun() return G def get_operations_disponibles( @@ -171,47 +172,178 @@ def modifier_produit( """ 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 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: return G # Obtention du produit sélectionné prod = sel_display + + # Trouver l'index du produit dans la sauvegarde + index_key = None 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 # Suppression du produit si demandé if st.button(f"{str(_('pages.personnalisation.delete'))} {prod}"): - supprime_champ_statut(f"pages.personnalisation.create_product.{index}") - if index == 0: - supprime_champ_statut("pages.personnalisation.create_product") + if index_key is not None: + # Supprimer les données du produit + 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) # Gestion des opérations d'assemblage ops_dispo = get_operations_disponibles(G) 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 niveau1 = get_composants_niveau1(G) 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é if st.button(f"{str(_('pages.personnalisation.update'))} {prod}"): G = mettre_a_jour_operations(G, prod, curr_ops, sel_op) - if sel_op: - maj_champ_statut(f"pages.personnalisation.create_product.{index}.edge", sel_op) + if index_key is not None: + 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) - if nouveaux: - supprime_champ_statut(f"pages.personnalisation.create_product.{index}.composants") - for j, comp in enumerate(nouveaux): - maj_champ_statut(f"pages.personnalisation.create_product.{index}.composants.{j}", comp) + if index_key is not None: + if nouveaux: + supprime_champ_statut(f"pages.personnalisation.create_product.{index_key}.composants") + for j, comp in enumerate(nouveaux): + 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'))}") + # Forcer la sauvegarde de l'état + maj_champ_statut("pages.personnalisation.create_product.fait", "oui") return G diff --git a/assets/locales/en.json b/assets/locales/en.json index ce0c7d1..d39dbfc 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -66,6 +66,7 @@ "add_new_product": "Add a new final product", "new_product_name": "New product name (unique)", "assembly_operation": "Assembly operation (optional)", + "product_exists": "This product already exists", "none": "-- None --", "components_to_link": "Components to link", "create_product": "Create product", diff --git a/assets/locales/fr.json b/assets/locales/fr.json index a37d1a8..1b872e5 100644 --- a/assets/locales/fr.json +++ b/assets/locales/fr.json @@ -66,6 +66,7 @@ "add_new_product": "Ajouter un nouveau produit final", "new_product_name": "Nom du nouveau produit (unique)", "assembly_operation": "Opération d'assemblage (optionnelle)", + "product_exists": "Ce produit existe déjà.", "none": "-- Aucune --", "components_to_link": "Composants à lier", "create_product": "Créer le produit",