diff --git a/app/forms/main/create_bug_report.py b/app/forms/main/create_bug_report.py new file mode 100644 index 000000000..e94920f85 --- /dev/null +++ b/app/forms/main/create_bug_report.py @@ -0,0 +1,66 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# ScoDoc +# +# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Emmanuel Viennet emmanuel.viennet@viennet.net +# +############################################################################## + +""" +Formulaire création de ticket de bug +""" + +from flask_wtf import FlaskForm +from wtforms import SubmitField, validators +from wtforms.fields.simple import StringField, TextAreaField, BooleanField +from app.scodoc import sco_preferences + + +class CreateBugReport(FlaskForm): + """Formulaire permettant la création d'un ticket de bug""" + + title = StringField( + label="Titre du ticket", + validators=[ + validators.DataRequired("titre du ticket requis"), + ], + ) + message = TextAreaField( + label="Message", + id="ticket_message", + validators=[ + validators.DataRequired("message du ticket requis"), + ], + ) + etab = StringField(label="Etablissement") + include_dump = BooleanField( + """Inclure une copie anonymisée de la base de données ? + Ces données faciliteront le traitement du problème et resteront strictement confidentielles. + """, + default=False, + ) + submit = SubmitField("Envoyer") + cancel = SubmitField("Annuler", render_kw={"formnovalidate": True}) + + def __init__(self, *args, **kwargs): + super(CreateBugReport, self).__init__(*args, **kwargs) + self.etab.data = sco_preferences.get_preference("InstituteName") or "" diff --git a/app/scodoc/sco_bug_report.py b/app/scodoc/sco_bug_report.py new file mode 100644 index 000000000..c7df1a111 --- /dev/null +++ b/app/scodoc/sco_bug_report.py @@ -0,0 +1,102 @@ +# -*- mode: python -*- +# -*- coding: utf-8 -*- + +############################################################################## +# +# Gestion scolarite IUT +# +# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Emmanuel Viennet emmanuel.viennet@viennet.net +# +############################################################################## + +"""Rapport de bug ScoDoc + +Permet de créer un rapport de bug (ticket) sur la plateforme git scodoc.org. + +Le principe est le suivant: + 1- Si l'utilisateur le demande, on dump la base de données et on l'envoie + + 2- ScoDoc envoie une requête POST à scodoc.org pour qu'un ticket git soit créé avec les + informations fournies par l'utilisateur + quelques métadonnées. + +""" +from flask import g +from flask_login import current_user +import requests + +import app.scodoc.sco_utils as scu +import sco_version + +from app import log +from app.scodoc.sco_dump_db import sco_dump_and_send_db +from app.scodoc.sco_exceptions import ScoValueError + + +def sco_bug_report( + title: str = "", message: str = "", etab: str = "", include_dump: bool = False +) -> requests.Response: + """Envoi d'un bug report (ticket)""" + dump_id = None + + if include_dump: + dump = sco_dump_and_send_db() + + try: + dump_id = dump.json()["dump_id"] + except (requests.exceptions.JSONDecodeError, KeyError): + dump_id = "inconnu (erreur)" + + log(f"sco_bug_report: {scu.SCO_BUG_REPORT_URL} by {current_user.user_name}") + try: + r = requests.post( + scu.SCO_BUG_REPORT_URL, + json={ + "ticket": { + "title": title, + "message": message, + "etab": etab, + "dept": getattr(g, "scodoc_dept", "-"), + }, + "user": { + "name": current_user.get_nomcomplet(), + "email": current_user.email, + }, + "dump": { + "included": include_dump, + "id": dump_id, + }, + "scodoc": { + "version": sco_version.SCOVERSION, + }, + }, + timeout=scu.SCO_ORG_TIMEOUT, + ) + + except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as exc: + log("ConnectionError: Impossible de joindre le serveur d'assistance") + raise ScoValueError( + """ + Impossible de joindre le serveur d'assistance (scodoc.org). + Veuillez contacter le service informatique de votre établissement pour + corriger la configuration de ScoDoc. Dans la plupart des cas, il + s'agit d'un proxy mal configuré. + """ + ) from exc + + return r diff --git a/app/scodoc/sco_dump_db.py b/app/scodoc/sco_dump_db.py index 1bef54391..b7a5f8aba 100644 --- a/app/scodoc/sco_dump_db.py +++ b/app/scodoc/sco_dump_db.py @@ -67,7 +67,7 @@ SCO_DUMP_LOCK = "/tmp/scodump.lock" def sco_dump_and_send_db( message: str = "", request_url: str = "", traceback_str_base64: str = "" -): +) -> requests.Response: """Dump base de données et l'envoie anonymisée pour debug""" traceback_str = base64.urlsafe_b64decode(traceback_str_base64).decode( scu.SCO_ENCODING @@ -97,7 +97,6 @@ def sco_dump_and_send_db( # Send r = _send_db(ano_db_name, message, request_url, traceback_str=traceback_str) - code = r.status_code finally: # Drop anonymized database @@ -107,7 +106,7 @@ def sco_dump_and_send_db( log("sco_dump_and_send_db: done.") - return code + return r def _duplicate_db(db_name, ano_db_name): diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 4178c0de3..f4130cb4a 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -719,6 +719,7 @@ SCO_DEV_MAIL = "emmanuel.viennet@gmail.com" # SVP ne pas changer # ne pas changer (ou vous perdez le support) SCO_DUMP_UP_URL = "https://scodoc.org/scodoc-installmgr/upload-dump" SCO_UP2DATE = "https://scodoc.org/scodoc-installmgr/check_version" +SCO_BUG_REPORT_URL = "https://scodoc.org/scodoc-installmgr/report" SCO_ORG_TIMEOUT = 180 # contacts scodoc.org SCO_EXT_TIMEOUT = 180 # appels à des ressources extérieures (siret, ...) SCO_TEST_API_TIMEOUT = 5 # pour tests unitaires API diff --git a/app/templates/sco_bug_report.j2 b/app/templates/sco_bug_report.j2 new file mode 100644 index 000000000..07bd189c9 --- /dev/null +++ b/app/templates/sco_bug_report.j2 @@ -0,0 +1,19 @@ +{# -*- mode: jinja-html -*- #} +{% extends 'base.j2' %} +{% import 'wtf.j2' as wtf %} + +{% block app_content %} +
+ Ce formulaire permet d'effectuer une demande d'assistance technique.
+ Son contenu sera accessible publiquement sur scodoc.org, veuillez donc ne pas y inclure d'informations sensibles.
+ L'adresse email associée à votre compte ScoDoc est automatiquement transmise avec votre demande mais ne sera pas
+ affichée publiquement.
+
- Erreur: espace serveur trop plein. - Merci de contacter {0}
""".format( - scu.SCO_DEV_MAIL - ) - ) - elif status_code == requests.codes.OK: # pylint: disable=no-member - H.append("""Opération effectuée.
""") + if status_code == requests.codes.OK: # pylint: disable=no-member + H.append(f"""Opération effectuée.
{r_msg}
""") else: - H.append( - f"""- Erreur: code {status_code} - Merci de contacter {scu.SCO_DEV_MAIL}
""" - ) + H.append(f"""{r_msg}
""") flash("Données envoyées au serveur d'assistance") return "\n".join(H) + html_sco_header.sco_footer() + + +# --- Report form (assistance) +@bp.route("/sco_bug_report", methods=["GET", "POST"]) +@scodoc +@permission_required(Permission.ScoView) +def sco_bug_report_form(): + "Formulaire de création d'un ticket d'assistance" + + form = CreateBugReport() + if request.method == "POST" and form.cancel.data: # cancel button + return flask.redirect(url_for("scodoc.index")) + if form.validate_on_submit(): + r = sco_bug_report.sco_bug_report( + form.title.data, form.message.data, form.etab.data, form.include_dump.data + ) + + status_code = r.status_code + try: + r_msg = r.json()["message"] + except (requests.exceptions.JSONDecodeError, KeyError): + log(f"sco_bug_report: error {status_code}") + r_msg = f"""Erreur: code {status_code} + Merci de contacter + {scu.SCO_DEV_MAIL} + """ + + H = [html_sco_header.sco_header(page_title="Assistance technique")] + if r.status_code >= 200 and r.status_code < 300: + H.append(f"""Opération effectuée.
{r_msg}
""") + else: + H.append(f"""{r_msg}
""") + return "\n".join(H) + html_sco_header.sco_footer() + + return render_template( + "sco_bug_report.j2", + form=form, + )