module-retour-client-revo 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. module_retour_client_revo-1.0.0/.gitignore +103 -0
  2. module_retour_client_revo-1.0.0/PKG-INFO +117 -0
  3. module_retour_client_revo-1.0.0/README.md +98 -0
  4. module_retour_client_revo-1.0.0/pyproject.toml +39 -0
  5. module_retour_client_revo-1.0.0/retour_client/__init__.py +1 -0
  6. module_retour_client_revo-1.0.0/retour_client/adapters.py +35 -0
  7. module_retour_client_revo-1.0.0/retour_client/admin.py +14 -0
  8. module_retour_client_revo-1.0.0/retour_client/apps.py +6 -0
  9. module_retour_client_revo-1.0.0/retour_client/constantes.py +65 -0
  10. module_retour_client_revo-1.0.0/retour_client/context_processors.py +48 -0
  11. module_retour_client_revo-1.0.0/retour_client/forms/__init__.py +2 -0
  12. module_retour_client_revo-1.0.0/retour_client/forms/commentaires_bug.py +31 -0
  13. module_retour_client_revo-1.0.0/retour_client/forms/retour_client.py +16 -0
  14. module_retour_client_revo-1.0.0/retour_client/http_utils.py +28 -0
  15. module_retour_client_revo-1.0.0/retour_client/management/__init__.py +0 -0
  16. module_retour_client_revo-1.0.0/retour_client/management/commands/__init__.py +0 -0
  17. module_retour_client_revo-1.0.0/retour_client/management/commands/envoi_mail_retour_client_preprod.py +15 -0
  18. module_retour_client_revo-1.0.0/retour_client/management/envoi_mail_retour_client_preprod.py +76 -0
  19. module_retour_client_revo-1.0.0/retour_client/migrations/__init__.py +0 -0
  20. module_retour_client_revo-1.0.0/retour_client/models/__init__.py +2 -0
  21. module_retour_client_revo-1.0.0/retour_client/models/reponse_bug.py +23 -0
  22. module_retour_client_revo-1.0.0/retour_client/models/retour_client.py +94 -0
  23. module_retour_client_revo-1.0.0/retour_client/services/__init__.py +0 -0
  24. module_retour_client_revo-1.0.0/retour_client/services/statuts_bug.py +85 -0
  25. module_retour_client_revo-1.0.0/retour_client/templates/css/kanban.css +188 -0
  26. module_retour_client_revo-1.0.0/retour_client/templates/css/pipeline.css +280 -0
  27. module_retour_client_revo-1.0.0/retour_client/templates/css/retour_client.css +281 -0
  28. module_retour_client_revo-1.0.0/retour_client/templates/detail-retour-client.html +119 -0
  29. module_retour_client_revo-1.0.0/retour_client/templates/formulaire_edition_modal.html +3 -0
  30. module_retour_client_revo-1.0.0/retour_client/templates/kanban-retour-client.html +163 -0
  31. module_retour_client_revo-1.0.0/retour_client/templates/liste-retour-client.html +17 -0
  32. module_retour_client_revo-1.0.0/retour_client/templates/mails/nouveau-ticket-client.tpl +1 -0
  33. module_retour_client_revo-1.0.0/retour_client/templates/mails/recap-journalier-client.tpl +1 -0
  34. module_retour_client_revo-1.0.0/retour_client/templates/mails/recap-journalier-revo.tpl +1 -0
  35. module_retour_client_revo-1.0.0/retour_client/templates/mails/reponse-ticket-client.tpl +1 -0
  36. module_retour_client_revo-1.0.0/retour_client/templates/pipeline-retour-client.html +60 -0
  37. module_retour_client_revo-1.0.0/retour_client/templates/recherche-filtre-bug.html +37 -0
  38. module_retour_client_revo-1.0.0/retour_client/templates/retour_client.html +304 -0
  39. module_retour_client_revo-1.0.0/retour_client/urls.py +25 -0
  40. module_retour_client_revo-1.0.0/retour_client/utils/__init__.py +1 -0
  41. module_retour_client_revo-1.0.0/retour_client/utils/anymail_utils.py +90 -0
  42. module_retour_client_revo-1.0.0/retour_client/views/__init__.py +2 -0
  43. module_retour_client_revo-1.0.0/retour_client/views/retour_client.py +571 -0
@@ -0,0 +1,103 @@
1
+ # General #
2
+ .htaccess
3
+ htaccess
4
+ tmp/
5
+ #public/
6
+ logs/
7
+ stderr.log
8
+ .DS_Store
9
+
10
+ # Django #
11
+ *.log
12
+ *.pot
13
+ *.pyc
14
+ *.db
15
+ __pycache__
16
+ db.sqlite3
17
+ media
18
+ staticfiles
19
+
20
+ # Backup files #
21
+ *.bak
22
+
23
+ # If you are using PyCharm #
24
+ */.idea/*
25
+ .idea
26
+ .idea/**/workspace.xml
27
+ .idea/**/tasks.xml
28
+ .idea/dictionaries
29
+ .idea/**/dataSources/
30
+ .idea/**/dataSources.ids
31
+ .idea/**/dataSources.xml
32
+ .idea/**/dataSources.local.xml
33
+ .idea/**/sqlDataSources.xml
34
+ .idea/**/dynamic.xml
35
+ .idea/**/uiDesigner.xml
36
+ .idea/**/gradle.xml
37
+ .idea/**/libraries
38
+ *.iws /out/
39
+
40
+ # If your are using VSCode #
41
+ .vscode/*
42
+ !.vscode/settings.json
43
+ !.vscode/tasks.json
44
+ !.vscode/launch.json
45
+ !.vscode/extensions.json
46
+ !.vscode/*.code-snippets
47
+ .history/
48
+ *.vsix
49
+ .vscode/
50
+
51
+ # Python #
52
+ *.py[cod]
53
+ *$py.class
54
+
55
+ # Distribution / packaging
56
+ .Python build/
57
+ develop-eggs/
58
+ downloads/
59
+ eggs/
60
+ .eggs/
61
+ #lib/
62
+ lib64/
63
+ parts/
64
+ sdist/
65
+ var/
66
+ wheels/
67
+ *.egg-info/
68
+ .installed.cfg
69
+ *.egg
70
+ *.manifest
71
+ *.spec
72
+
73
+ # Installer logs
74
+ pip-log.txt
75
+ pip-delete-this-directory.txt
76
+
77
+ # Unit test / coverage reports
78
+ htmlcov/
79
+ .tox/
80
+ .coverage
81
+ .coverage.*
82
+ .cache
83
+ .pytest_cache/
84
+ nosetests.xml
85
+ coverage.xml
86
+ *.cover
87
+ .hypothesis/
88
+
89
+ # Jupyter Notebook
90
+ .ipynb_checkpoints
91
+
92
+ # pyenv
93
+ .python-version
94
+
95
+
96
+ # Environments
97
+ .env
98
+ .venv
99
+ env/
100
+ venv/
101
+ ENV/
102
+ env.bak/
103
+ venv.bak/
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: module-retour-client-revo
3
+ Version: 1.0.0
4
+ Summary: Module Django pour la gestion des retours clients et bugs
5
+ Author-email: Revolucy <hello@revolucy.fr>
6
+ License: MIT
7
+ Classifier: Framework :: Django
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Python: >=3.8
15
+ Requires-Dist: django-anymail[brevo]>=12.0
16
+ Requires-Dist: django<6.0,>=4.2
17
+ Requires-Dist: requests<3,>=2.31
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Module de retour Client
21
+
22
+ ## Conception
23
+
24
+ - [Django](https://www.djangoproject.com/)
25
+ - [Bootstrap](https://getbootstrap.com/)
26
+ - [JQuery 3.5.1](https://api.jquery.com/category/version/3.5/)
27
+
28
+ ## Installation
29
+
30
+ #### 1 - Ajouter le package dans les requirements
31
+ #### 2 - Ajouter dans votre .env :
32
+ => Pour la préprod/Prod :<br>
33
+ ```html
34
+ #====================================== RETOUR CLIENT =======================================
35
+ EMAIL_CLIENT_RETOUR='test@test.fr'
36
+ SIREN_CLIENT='1245648'
37
+ DATE_MISE_EN_PRODUCTION='2024-12-30'
38
+ DATE_FIN_GARANTIE='2024-12-30'
39
+ ```
40
+
41
+ #### 3 - Ajouter dans votre fichier settings.py :
42
+ ```html
43
+ INSTALLED_APPS = [
44
+ 'retour_client',
45
+ ]
46
+ ```
47
+ ```html
48
+ 'context_processors': [
49
+ 'retour_client.context_processors.formulaire_retour_client',
50
+ ]
51
+ ```
52
+ ```html
53
+ ########### Configuration RETOUR CLIENT.
54
+ EMAIL_CLIENT_RETOUR = env('EMAIL_CLIENT_RETOUR')
55
+ SIREN_CLIENT = env('SIREN_CLIENT')
56
+ DATE_MISE_EN_PRODUCTION = env('DATE_MISE_EN_PRODUCTION')
57
+ DATE_FIN_GARANTIE = env('DATE_FIN_GARANTIE')
58
+ ```
59
+ #### 4 - Ajouter dans votre fichier urls.py :
60
+ ```html
61
+ path('retour-client/', include("retour_client.urls")),
62
+ ```
63
+ #### 5 - Ajouter dans le footer.html de votre base :
64
+ ```html
65
+ <!-- AJOUT POPUP RETOUR CLIENT -->
66
+ {% include 'retour_client.html' %}
67
+ ```
68
+ #### 6 - Ajouter un cron journalier 09h00 sur le serveur pour envoi des mails en préprod:
69
+ ```html
70
+ Commande : envoi_mail_retour_client_preprod
71
+ ```
72
+
73
+ ## Licence
74
+
75
+ [Revolucy](https://www.revolucy.fr)
76
+
77
+ ## Versionning
78
+
79
+ - V1.0.0 | Création du module de retour
80
+
81
+ ## Flux
82
+
83
+ ### PREPROD :
84
+ => Module de retour visible pour tous les utilisateurs
85
+
86
+ 1 - Création d'un ticket par le client :
87
+ - Enregistrement du ticket sur le site
88
+ - 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
89
+
90
+ ### PRODUCTION - EN PERIODE DE GARANTIE :
91
+ => Module de retour visible pour les administrateurs seulement
92
+ => Affichage du compteur de jours restant de garantie
93
+
94
+ 1 - Création d'un ticket par le client :
95
+ - Enregistrement du ticket sur le site
96
+ - 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
97
+
98
+ ### PRODUCTION - HORS PERIODE DE GARANTIE :
99
+ => Module de retour visible pour les administrateurs seulement
100
+ => Affichage du compteur de nombre de crédit ticket restant
101
+ => Si moins d’1H de crédit : Ajout d'un lien pour prendre un pack de crédit 5H ou 10H, puis création de la facture associée dans Lucy
102
+
103
+ 1 - Création d'un ticket par le client :
104
+ - Enregistrement du ticket sur le site
105
+ - Mail Chef de projet revo à l'ajout du ticket
106
+
107
+ 2 - Réponse par le chef de projet :
108
+ - Indication du nombre d'heure passé
109
+ - Déduction du crédit de temps dans Lucy par API
110
+ - Mail Client à l'ajout d'une réponse au ticket
111
+
112
+ ## Déploiement Pypi
113
+
114
+ 1 - pip install build twine<br>
115
+ 2 - python -m build<br>
116
+ 3 - python -m twine upload dist/*<br>
117
+ 4 - Indiquer le token présent dans 1Password
@@ -0,0 +1,98 @@
1
+ # Module de retour Client
2
+
3
+ ## Conception
4
+
5
+ - [Django](https://www.djangoproject.com/)
6
+ - [Bootstrap](https://getbootstrap.com/)
7
+ - [JQuery 3.5.1](https://api.jquery.com/category/version/3.5/)
8
+
9
+ ## Installation
10
+
11
+ #### 1 - Ajouter le package dans les requirements
12
+ #### 2 - Ajouter dans votre .env :
13
+ => Pour la préprod/Prod :<br>
14
+ ```html
15
+ #====================================== RETOUR CLIENT =======================================
16
+ EMAIL_CLIENT_RETOUR='test@test.fr'
17
+ SIREN_CLIENT='1245648'
18
+ DATE_MISE_EN_PRODUCTION='2024-12-30'
19
+ DATE_FIN_GARANTIE='2024-12-30'
20
+ ```
21
+
22
+ #### 3 - Ajouter dans votre fichier settings.py :
23
+ ```html
24
+ INSTALLED_APPS = [
25
+ 'retour_client',
26
+ ]
27
+ ```
28
+ ```html
29
+ 'context_processors': [
30
+ 'retour_client.context_processors.formulaire_retour_client',
31
+ ]
32
+ ```
33
+ ```html
34
+ ########### Configuration RETOUR CLIENT.
35
+ EMAIL_CLIENT_RETOUR = env('EMAIL_CLIENT_RETOUR')
36
+ SIREN_CLIENT = env('SIREN_CLIENT')
37
+ DATE_MISE_EN_PRODUCTION = env('DATE_MISE_EN_PRODUCTION')
38
+ DATE_FIN_GARANTIE = env('DATE_FIN_GARANTIE')
39
+ ```
40
+ #### 4 - Ajouter dans votre fichier urls.py :
41
+ ```html
42
+ path('retour-client/', include("retour_client.urls")),
43
+ ```
44
+ #### 5 - Ajouter dans le footer.html de votre base :
45
+ ```html
46
+ <!-- AJOUT POPUP RETOUR CLIENT -->
47
+ {% include 'retour_client.html' %}
48
+ ```
49
+ #### 6 - Ajouter un cron journalier 09h00 sur le serveur pour envoi des mails en préprod:
50
+ ```html
51
+ Commande : envoi_mail_retour_client_preprod
52
+ ```
53
+
54
+ ## Licence
55
+
56
+ [Revolucy](https://www.revolucy.fr)
57
+
58
+ ## Versionning
59
+
60
+ - V1.0.0 | Création du module de retour
61
+
62
+ ## Flux
63
+
64
+ ### PREPROD :
65
+ => Module de retour visible pour tous les utilisateurs
66
+
67
+ 1 - Création d'un ticket par le client :
68
+ - Enregistrement du ticket sur le site
69
+ - 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
70
+
71
+ ### PRODUCTION - EN PERIODE DE GARANTIE :
72
+ => Module de retour visible pour les administrateurs seulement
73
+ => Affichage du compteur de jours restant de garantie
74
+
75
+ 1 - Création d'un ticket par le client :
76
+ - Enregistrement du ticket sur le site
77
+ - 2 Mails (Client / Chef de projet revo) journalier avec liste des tickets en attente de traitement avec les retours via un Batch
78
+
79
+ ### PRODUCTION - HORS PERIODE DE GARANTIE :
80
+ => Module de retour visible pour les administrateurs seulement
81
+ => Affichage du compteur de nombre de crédit ticket restant
82
+ => Si moins d’1H de crédit : Ajout d'un lien pour prendre un pack de crédit 5H ou 10H, puis création de la facture associée dans Lucy
83
+
84
+ 1 - Création d'un ticket par le client :
85
+ - Enregistrement du ticket sur le site
86
+ - Mail Chef de projet revo à l'ajout du ticket
87
+
88
+ 2 - Réponse par le chef de projet :
89
+ - Indication du nombre d'heure passé
90
+ - Déduction du crédit de temps dans Lucy par API
91
+ - Mail Client à l'ajout d'une réponse au ticket
92
+
93
+ ## Déploiement Pypi
94
+
95
+ 1 - pip install build twine<br>
96
+ 2 - python -m build<br>
97
+ 3 - python -m twine upload dist/*<br>
98
+ 4 - Indiquer le token présent dans 1Password
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.20"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "module-retour-client-revo"
7
+ version = "1.0.0"
8
+ description = "Module Django pour la gestion des retours clients et bugs"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{name = "Revolucy", email = "hello@revolucy.fr"}]
13
+
14
+ dependencies = [
15
+ "Django>=4.2,<6.0",
16
+ "requests>=2.31,<3",
17
+ "django-anymail[brevo]>=12.0",
18
+ ]
19
+ classifiers = [
20
+ "Framework :: Django",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Operating System :: OS Independent",
27
+ ]
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["retour_client"]
31
+ include = [
32
+ "retour_client/templates/**",
33
+ "retour_client/static/**",
34
+ "retour_client/migrations/**",
35
+ "retour_client/management/**",
36
+ ]
37
+
38
+ [tool.hatch.version]
39
+ path = "retour_client/__init__.py"
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0"
@@ -0,0 +1,35 @@
1
+ from django.conf import settings
2
+ from django.core.mail import EmailMultiAlternatives
3
+ from django.utils import timezone
4
+
5
+
6
+ def now_date():
7
+ return timezone.now().date()
8
+
9
+
10
+ def is_user_admin(user):
11
+ fn = getattr(settings, "RETOUR_CLIENT_IS_ADMIN_FN", None)
12
+ return fn(user) if fn else bool(getattr(user, "is_staff", False))
13
+
14
+
15
+ def get_current_user(request):
16
+ return getattr(request, "user", None)
17
+
18
+
19
+ def image_upload_to(instance, filename):
20
+ fn = getattr(settings, "RETOUR_CLIENT_IMAGE_UPLOAD_TO", None)
21
+ return fn(instance, filename) if fn else f"retour_client/{filename}"
22
+
23
+
24
+ def send_mail_html(subject, to, body_html):
25
+ fn = getattr(settings, "RETOUR_CLIENT_SEND_MAIL_FN", None)
26
+ if fn:
27
+ return fn(subject, to, body_html)
28
+ mail = EmailMultiAlternatives(subject, body_html, to=to)
29
+ mail.attach_alternative(body_html, "text/html")
30
+ mail.send()
31
+
32
+
33
+ def get_lucy_collaborateur_email(siren):
34
+ fn = getattr(settings, "RETOUR_CLIENT_GET_COLLAB_EMAIL_FN", None)
35
+ return fn(siren) if fn else None
@@ -0,0 +1,14 @@
1
+ from django.contrib import admin
2
+
3
+ from retour_client.models import RetourClient, ReponseBug
4
+
5
+
6
+ class BlockReponseBug(admin.StackedInline):
7
+ model = ReponseBug
8
+ extra = 1
9
+
10
+
11
+ @admin.register(RetourClient)
12
+ class RetourClientAdmin(admin.ModelAdmin):
13
+ list_display = ('pk', 'priorite', 'statut', 'description')
14
+ inlines = [BlockReponseBug]
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class RetourClientConfig(AppConfig):
5
+ default_auto_field = 'django.db.models.BigAutoField'
6
+ name = 'retour_client'
@@ -0,0 +1,65 @@
1
+ class RetourClientConstantes:
2
+ INCONNU = 'Inconnu'
3
+
4
+ CRITIQUE = 'C'
5
+ NORMAL = 'N'
6
+ NON_URGENT = 'B'
7
+
8
+ priorite = [
9
+ (CRITIQUE, 'Critique'),
10
+ (NORMAL, 'Normal'),
11
+ (NON_URGENT, 'Non Urgent'),
12
+ ]
13
+
14
+ EN_ATTENTE = 'A'
15
+ EN_TRAITEMENT = 'E'
16
+ TRAITE = 'T'
17
+ VALIDE = 'V'
18
+ V2 = 'Z'
19
+
20
+ statut = [
21
+ (EN_ATTENTE, 'En attente'),
22
+ (EN_TRAITEMENT, 'En cours'),
23
+ (TRAITE, 'Traité par Revolucy'),
24
+ (VALIDE, 'Retour validé'),
25
+ (V2, 'Demande V2'),
26
+ ]
27
+
28
+ statut_client = [
29
+ (EN_ATTENTE, 'En attente'),
30
+ (EN_TRAITEMENT, 'En cours'),
31
+ (TRAITE, 'Traité par Revolucy'),
32
+ (V2, 'Demande V2'),
33
+ ]
34
+
35
+ NON_CLASSE = 'N'
36
+ QUESTION = 'Q'
37
+ BUG = 'B'
38
+ AMELIORATION = 'A'
39
+
40
+ type = [
41
+ (NON_CLASSE, 'Non classé'),
42
+ (QUESTION, 'Question'),
43
+ (BUG, 'Bug'),
44
+ (AMELIORATION, 'Amélioration'),
45
+ ]
46
+
47
+ STATUS_MAPPING_YT = {
48
+ 'En attente': 'A',
49
+ 'En cours': 'E',
50
+ 'Terminé': 'T',
51
+ }
52
+
53
+ REVOLUCY = 'R'
54
+ CLIENT = 'C'
55
+
56
+ repondant = [
57
+ (REVOLUCY, 'Revolucy'),
58
+ (CLIENT, 'Client'),
59
+ ]
60
+
61
+ class Environnement:
62
+ LOCAL = 'local'
63
+ RECETTE = 'recette'
64
+ PREPRODUCTION = 'preproduction'
65
+ PRODUCTION = 'production'
@@ -0,0 +1,48 @@
1
+ import logging
2
+ from datetime import datetime
3
+
4
+ from django.conf import settings
5
+
6
+ from .constantes import RetourClientConstantes
7
+ from .forms import RetourClientForm
8
+ from .http_utils import safe_get_json
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ def formulaire_retour_client(request):
14
+ form_bug = RetourClientForm()
15
+ jrs_restant_garantie = 0
16
+ solde_ticket = 0
17
+ current_user = request.user
18
+
19
+ if not request.user.is_anonymous and current_user.email == 'info.marseille@alyseparking.com':
20
+ siren = settings.SIREN_CLIENT_MARSEILLE
21
+ else:
22
+ siren = settings.SIREN_CLIENT
23
+
24
+ if (settings.ENVIRONNEMENT == RetourClientConstantes.Environnement.PRODUCTION
25
+ and not current_user.is_anonymous
26
+ and getattr(current_user, "is_administrateur", False)):
27
+
28
+ jrs_restant_garantie = (
29
+ datetime.strptime(settings.DATE_FIN_GARANTIE, '%Y-%m-%d').date()
30
+ - datetime.today().date()
31
+ ).days
32
+
33
+ url = f"https://app.lucy-crm.fr/api/credit-client/{siren}"
34
+
35
+ data, err = safe_get_json(url, timeout=(2, 5)) # ≤ ~5 s
36
+ if data:
37
+ solde_ticket = data.get("solde_ticket", 0)
38
+ else:
39
+ # On loggue mais on NE BLOQUE PAS : on garde solde_ticket=0
40
+ logger.error(f"[Lucy API] Impossible de récupérer solde_ticket: {err}")
41
+
42
+ return {
43
+ 'form_bug': form_bug,
44
+ 'environnement': settings.ENVIRONNEMENT,
45
+ 'siren_client': siren,
46
+ 'jrs_restant_garantie': jrs_restant_garantie,
47
+ 'solde_ticket': solde_ticket,
48
+ }
@@ -0,0 +1,2 @@
1
+ from .commentaires_bug import ReponseBugForm
2
+ from .retour_client import RetourClientForm
@@ -0,0 +1,31 @@
1
+ from django import forms
2
+
3
+ from retour_client.constantes import RetourClientConstantes
4
+ from retour_client.models import ReponseBug
5
+ from retour_client.adapters import is_user_admin
6
+
7
+
8
+ class ReponseBugForm(forms.ModelForm):
9
+ class Meta:
10
+ model = ReponseBug
11
+ fields = ["reponse", "repondant", "temps_passe", "piece_jointe"]
12
+
13
+ reponse = forms.CharField(
14
+ widget=forms.Textarea(attrs={"class": "form-control", "rows": 6}),
15
+ required=True,
16
+ )
17
+
18
+ def __init__(self, *args, **kwargs):
19
+ # récupère l’utilisateur sans casser l’API Django/admin
20
+ current_user = kwargs.pop("current_user", None)
21
+ super().__init__(*args, **kwargs)
22
+
23
+ # valeur initiale selon le rôle
24
+ if current_user and not getattr(current_user, "is_anonymous", False) and is_user_admin(current_user):
25
+ self.fields["repondant"].initial = RetourClientConstantes.REVOLUCY
26
+ else:
27
+ self.fields["repondant"].initial = RetourClientConstantes.CLIENT
28
+
29
+ def clean_reponse(self):
30
+ reponse = self.cleaned_data.get("reponse", "")
31
+ return reponse.replace("\n", "<br>")
@@ -0,0 +1,16 @@
1
+ from django import forms
2
+
3
+ from retour_client.models import RetourClient
4
+
5
+
6
+ class RetourClientForm(forms.ModelForm):
7
+ class Meta:
8
+ model = RetourClient
9
+ exclude = ['date_ajout', 'statut', 'utilisateur', 'image', 'navigateur', 'lien_url', 'type']
10
+
11
+ description = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 6}), required=True)
12
+
13
+ def clean_description(self):
14
+ description = self.cleaned_data.get('description')
15
+ # Convertit les sauts de ligne en <br> dans la description
16
+ return description.replace('\n', '<br>')
@@ -0,0 +1,28 @@
1
+ import requests
2
+ from requests.exceptions import RequestException, Timeout, ConnectionError
3
+
4
+ DEFAULT_TIMEOUT = (2, 5) # (connect_timeout, read_timeout) -> total ≤ ~5s
5
+
6
+
7
+ def safe_get_json(url, headers=None, timeout=DEFAULT_TIMEOUT):
8
+ try:
9
+ r = requests.get(url, headers=headers or {}, timeout=timeout)
10
+ if r.ok:
11
+ return r.json(), None
12
+ return None, f"HTTP {r.status_code}"
13
+ except (Timeout, ConnectionError) as e:
14
+ return None, f"timeout/conn error: {e}"
15
+ except RequestException as e:
16
+ return None, f"request error: {e}"
17
+ except ValueError as e: # JSON decode
18
+ return None, f"json decode error: {e}"
19
+
20
+
21
+ def safe_post_json(url, payload, headers=None, timeout=DEFAULT_TIMEOUT):
22
+ try:
23
+ r = requests.post(url, json=payload, headers=headers or {}, timeout=timeout)
24
+ return r, None
25
+ except (Timeout, ConnectionError) as e:
26
+ return None, f"timeout/conn error: {e}"
27
+ except RequestException as e:
28
+ return None, f"request error: {e}"
@@ -0,0 +1,15 @@
1
+ import logging
2
+
3
+ from django.core.management import BaseCommand
4
+
5
+ from retour_client.management.envoi_mail_retour_client_preprod import EnvoiMailRetourClientBatch
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class Command(BaseCommand):
11
+ help = "Envoyer les recap des tickets en cours au client et chef de projet"
12
+
13
+ def handle(self, *args, **options):
14
+ logger.info(f"Lancement de la commande '{self.help}'")
15
+ EnvoiMailRetourClientBatch.do_process()