Code/app/fiches/utils/tickets/creation.py

186 lines
8.0 KiB
Python

# creation.py
import re
import base64
import streamlit as st
from utils.translations import _
from .core import charger_fiches_et_labels, construire_corps_ticket_markdown, creer_ticket_gitea, get_labels_existants, nettoyer_labels
from config import ENV
import requests
def parser_modele_ticket(contenu_modele):
"""Parse le modèle de ticket en sections."""
sections = {}
lignes = contenu_modele.splitlines()
titre_courant, contenu = None, []
for ligne in lignes:
if ligne.startswith("## "):
if titre_courant:
sections[titre_courant] = "\n".join(contenu).strip()
titre_courant, contenu = ligne[3:].strip(), []
elif titre_courant:
contenu.append(ligne)
if titre_courant:
sections[titre_courant] = "\n".join(contenu).strip()
return sections
def generer_labels(fiche_selectionnee):
"""Génère les labels basés sur la fiche sélectionnée."""
labels, selected_ops = [], []
correspondances = charger_fiches_et_labels()
cible = correspondances.get(fiche_selectionnee)
if cible:
if len(cible["operations"]) == 1:
labels.append(cible["operations"][0])
elif len(cible["operations"]) > 1:
selected_ops = st.multiselect(str(_("pages.fiches.tickets.contribution_type")),
cible["operations"],
default=cible["operations"])
return labels, selected_ops, cible
def creer_champs_formulaire(sections, fiche_selectionnee):
"""Crée les champs du formulaire basés sur les sections."""
reponses = {}
for section, aide in sections.items():
if "Type de contribution" in section:
options = sorted(set(re.findall(r"- \[.\] (.+)", aide)))
if str(_("pages.fiches.tickets.other")) not in options:
options.append(str(_("pages.fiches.tickets.other")))
choix = st.radio(str(_("pages.fiches.tickets.contribution_type")), options)
reponses[section] = st.text_input(str(_("pages.fiches.tickets.specify")), "") if choix == str(_("pages.fiches.tickets.other")) else choix
elif "Fiche concernée" in section:
url_fiche = f"https://fabnum-git.peccini.fr/FabNum/Fiches/src/branch/{ENV}/Documents/{fiche_selectionnee.replace(' ', '%20')}"
reponses[section] = url_fiche
st.text_input(str(_("pages.fiches.tickets.concerned_card")), value=url_fiche, disabled=True)
elif "Sujet de la proposition" in section:
reponses[section] = st.text_input(section, help=aide)
else:
reponses[section] = st.text_area(section, help=aide)
return reponses
def afficher_controles_formulaire():
"""Affiche les boutons de contrôle du formulaire."""
col1, col2 = st.columns(2)
if col1.button(str(_("pages.fiches.tickets.preview"))):
st.session_state.previsualiser = True
# S'assurer que l'expander reste ouvert en mode prévisualisation
st.session_state.expander_state = True
if col2.button(str(_("pages.fiches.tickets.cancel"))):
st.session_state.previsualiser = False
st.rerun()
def gerer_previsualisation_et_soumission(reponses, labels, selected_ops, cible):
"""Gère la prévisualisation et la soumission du ticket."""
# Si nous avons tenté de créer un ticket (succès ou erreur)
if st.session_state.get("ticket_cree", False) or st.session_state.get("ticket_erreur", False):
if st.session_state.get("ticket_cree", False):
st.success(str(_("pages.fiches.tickets.created_success")))
else:
st.error(str(_("pages.fiches.tickets.creation_error")))
if st.button(str(_("pages.fiches.tickets.continue"))):
# Réinitialiser le formulaire et cacher l'expander
st.session_state.ticket_cree = False
st.session_state.ticket_erreur = False
st.session_state.previsualiser = False
st.session_state.expander_state = False
st.rerun()
return
# Si nous ne sommes pas en mode prévisualisation, ne rien afficher
if not st.session_state.get("previsualiser", False):
return
st.subheader(str(_("pages.fiches.tickets.preview_title")))
for section, texte in reponses.items():
st.markdown(f"#### {section}")
st.code(texte, language="markdown")
titre_ticket = reponses.get("Sujet de la proposition", "").strip() or "Ticket FabNum"
final_labels = nettoyer_labels(labels + selected_ops + ([cible["item"]] if cible else []))
st.markdown(f"**{str(_('pages.fiches.tickets.summary'))} :**\n- **{str(_('pages.fiches.tickets.title'))}** : `{titre_ticket}`\n- **{str(_('pages.fiches.tickets.labels'))}** : `{', '.join(final_labels)}`")
if st.button(str(_("pages.fiches.tickets.confirm"))):
labels_existants = get_labels_existants()
labels_ids = [labels_existants[l] for l in final_labels if l in labels_existants]
if "Backlog" in labels_existants:
labels_ids.append(labels_existants["Backlog"])
corps = construire_corps_ticket_markdown(reponses)
resultat = creer_ticket_gitea(titre_ticket, corps, labels_ids)
# Marquer le résultat et ouvrir l'expander pour afficher le résultat
st.session_state.ticket_cree = resultat
st.session_state.ticket_erreur = not resultat
st.session_state.previsualiser = False
st.session_state.expander_state = True
st.rerun()
def formulaire_creation_ticket_dynamique(fiche_selectionnee):
"""Fonction principale pour le formulaire de création de ticket."""
# Initialiser l'état de l'expander si ce n'est pas déjà fait
if "expander_state" not in st.session_state:
st.session_state.expander_state = False
with st.expander(str(_("pages.fiches.tickets.create_new")), expanded=st.session_state.expander_state):
# Initialiser les états si ce n'est pas déjà fait
if "ticket_cree" not in st.session_state:
st.session_state.ticket_cree = False
if "ticket_erreur" not in st.session_state:
st.session_state.ticket_erreur = False
if "previsualiser" not in st.session_state:
st.session_state.previsualiser = False
# Chargement et vérification du modèle
contenu_modele = charger_modele_ticket()
if not contenu_modele:
st.error(str(_("pages.fiches.tickets.model_load_error")))
return
# Traitement du modèle et génération du formulaire
sections = parser_modele_ticket(contenu_modele)
labels, selected_ops, cible = generer_labels(fiche_selectionnee)
# Créer le formulaire et gérer ses états
if st.session_state.ticket_cree or st.session_state.ticket_erreur:
# Si le ticket a été créé ou a échoué, afficher le message approprié et le bouton continuer
gerer_previsualisation_et_soumission({}, labels, selected_ops, cible)
else:
# Sinon afficher le formulaire normal
reponses = creer_champs_formulaire(sections, fiche_selectionnee)
# Afficher les contrôles uniquement si nous ne sommes pas en mode prévisualisation
if not st.session_state.previsualiser:
afficher_controles_formulaire()
# Gérer la prévisualisation et soumission
gerer_previsualisation_et_soumission(reponses, labels, selected_ops, cible)
def charger_modele_ticket():
from config import GITEA_URL, GITEA_TOKEN, ORGANISATION, DEPOT_FICHES
headers = {"Authorization": f"token {GITEA_TOKEN}"}
url = f"{GITEA_URL}/repos/{ORGANISATION}/{DEPOT_FICHES}/contents/.gitea/ISSUE_TEMPLATE/Contenu.md"
try:
r = requests.get(url, headers=headers, timeout=10)
r.raise_for_status()
return base64.b64decode(r.json().get("content", "")).decode("utf-8")
except Exception as e:
st.error(f"{str(_('pages.fiches.tickets.model_error'))} {e}")
return ""