Norberto Montalvo García commited on
Commit
5c615df
·
unverified ·
2 Parent(s): 7be41d4 08b0b11

Merge pull request #38 from CascoArcilla/HU3

Browse files

Implementación de Sort, creacion de Napping, redefinicion de logica general y ajustes menores.

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. tecnicas/admin.py +2 -1
  2. tecnicas/controllers/__init__.py +21 -10
  3. tecnicas/controllers/api_controller/rating_sort_controller.py +67 -0
  4. tecnicas/controllers/models_controller/tecnica_controller.py +1 -1
  5. tecnicas/controllers/views_controller/create_session/panel_basic_controller.py +69 -9
  6. tecnicas/controllers/views_controller/create_session/panel_codes_controller.py +6 -6
  7. tecnicas/controllers/views_controller/create_session/panel_tags_controller.py +2 -2
  8. tecnicas/controllers/views_controller/create_session/panel_words_controller.py +1 -1
  9. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_cata_controller.py +137 -0
  10. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_controller.py +17 -0
  11. tecnicas/controllers/views_controller/create_session/{panel_create_controller.py → panels_create/panel_create_escalas_controller.py} +7 -378
  12. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_napping_controller.py +95 -0
  13. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_pf_controller.py +105 -0
  14. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_rata_controller.py +155 -0
  15. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_sort_controller.py +95 -0
  16. tecnicas/controllers/views_controller/session_management/{details_cata_controller.py → details/details_cata_controller.py} +1 -1
  17. tecnicas/controllers/views_controller/session_management/{details_controller.py → details/details_controller.py} +1 -1
  18. tecnicas/controllers/views_controller/session_management/{details_escala_controller.py → details/details_escala_controller.py} +4 -7
  19. tecnicas/controllers/views_controller/session_management/{details_pf_controller.py → details/details_pf_controller.py} +2 -7
  20. tecnicas/controllers/views_controller/session_management/{details_rata_controller.py → details/details_rata_controller.py} +0 -5
  21. tecnicas/controllers/views_controller/session_management/details/details_sort_controller.py +91 -0
  22. tecnicas/controllers/views_controller/session_management/{monitor_controller.py → monitor/monitor_controller.py} +5 -7
  23. tecnicas/controllers/views_controller/session_management/{monitor_escalas_controller.py → monitor/monitor_escalas_controller.py} +0 -0
  24. tecnicas/controllers/views_controller/session_management/{monitor_pf_controller.py → monitor/monitor_pf_controller.py} +0 -3
  25. tecnicas/controllers/views_controller/session_management/{monitor_rata_controller.py → monitor/monitor_rata_controller.py} +0 -0
  26. tecnicas/controllers/views_controller/session_management/monitor/monitor_sort_controller.py +27 -0
  27. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_controller.py +1 -1
  28. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_escalas_controller.py +1 -1
  29. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_pf_controller.py +1 -1
  30. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_rata_controller.py +1 -1
  31. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_sort_controller.py +77 -0
  32. tecnicas/controllers/views_controller/sessions_tester/login_session_tester_controller.py +46 -0
  33. tecnicas/controllers/views_controller/sessions_tester/tests_forms/general_test_controller.py +15 -1
  34. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_cata_controller.py +1 -1
  35. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_rata_controller.py +1 -1
  36. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_scales_controller.py +1 -1
  37. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_sort_controller.py +50 -0
  38. tecnicas/forms/__init__.py +2 -0
  39. tecnicas/forms/create_session/sesiob_basic_cata_form.py +7 -2
  40. tecnicas/forms/create_session/sesion_basic_form.py +7 -2
  41. tecnicas/forms/create_session/sesion_basic_napping.py +24 -0
  42. tecnicas/forms/create_session/sesion_basic_pf_form.py +0 -1
  43. tecnicas/forms/create_session/sesion_basic_sort_form.py +24 -0
  44. tecnicas/migrations/0024_alter_sesionsensorial_codigo_sesion_grupoproducto.py +30 -0
  45. tecnicas/models/__init__.py +1 -0
  46. tecnicas/models/grupo_producto.py +19 -0
  47. tecnicas/static/js/download-table-csv.js +1 -1
  48. tecnicas/static/js/download-table-sort-csv.js +79 -0
  49. tecnicas/static/js/test-sort.js +554 -0
  50. tecnicas/templates/tecnicas/components/table-scales.html +1 -1
tecnicas/admin.py CHANGED
@@ -14,7 +14,7 @@ from .models import Producto, Participacion
14
 
15
  from .models import Orden, Posicion
16
 
17
- from .models import Dato, ValorDecimal, ValorBooleano, Calificacion, ListaPalabras
18
 
19
  # Register your models here.
20
  admin.site.register(CategoriaTecnica)
@@ -47,3 +47,4 @@ admin.site.register(ValorDecimal)
47
  admin.site.register(ValorBooleano)
48
  admin.site.register(Calificacion)
49
  admin.site.register(ListaPalabras)
 
 
14
 
15
  from .models import Orden, Posicion
16
 
17
+ from .models import Dato, ValorDecimal, ValorBooleano, Calificacion, ListaPalabras, GrupoProducto
18
 
19
  # Register your models here.
20
  admin.site.register(CategoriaTecnica)
 
47
  admin.site.register(ValorBooleano)
48
  admin.site.register(Calificacion)
49
  admin.site.register(ListaPalabras)
50
+ admin.site.register(GrupoProducto)
tecnicas/controllers/__init__.py CHANGED
@@ -16,18 +16,26 @@ from .views_controller.create_session.panel_basic_controller import PanelBasicCo
16
  from .views_controller.create_session.panel_tags_controller import PanelTagsController
17
  from .views_controller.create_session.panel_codes_controller import PanelCodesController
18
  from .views_controller.create_session.panel_words_controller import PanelWordsController
19
- from .views_controller.create_session.panel_create_controller import PanelCreateController
20
 
21
- from .views_controller.session_management.details_controller import DetallesController
22
- from .views_controller.session_management.details_escala_controller import DetallesEscalasController
23
- from .views_controller.session_management.details_rata_controller import DetallesRATAController
24
- from .views_controller.session_management.details_cata_controller import DetallesCATAController
25
- from .views_controller.session_management.details_pf_controller import DetallesPFController
 
26
 
27
- from .views_controller.session_management.monitor_controller import MonitorController
28
- from .views_controller.session_management.monitor_escalas_controller import MonitorEscalasController
29
- from .views_controller.session_management.monitor_rata_controller import MonitorRATAController
30
- from .views_controller.session_management.monitor_pf_controller import MonitorPFController
 
 
 
 
 
 
 
 
31
 
32
  from .views_controller.sessions_tester.login_session_tester_controller import LoginSessionTesterController
33
  from .views_controller.sessions_tester.list_sessions_tester_controller import ListSessionsTesterController
@@ -36,10 +44,12 @@ from .views_controller.sessions_tester.tests_forms.test_scales_controller import
36
  from .views_controller.sessions_tester.tests_forms.test_rata_controller import TestRataController
37
  from .views_controller.sessions_tester.tests_forms.test_cata_controller import TestCataController
38
  from .views_controller.sessions_tester.tests_forms.test_pf_controller import TestPFController
 
39
 
40
  from .views_controller.sessions_tester.init_session.init_session_escalas_controller import InitSessionEscalasController
41
  from .views_controller.sessions_tester.init_session.init_session_rata_controller import InitSessionRATAController
42
  from .views_controller.sessions_tester.init_session.init_session_pf_controller import InitSessionPFController
 
43
 
44
  from .views_controller.vocabulary_manage.create_vocabulary_controller import CreateVocabularyController
45
  from .views_controller.vocabulary_manage.list_vocabulary_controller import ListVocabularyController
@@ -48,3 +58,4 @@ from .api_controller.rating_sacales_controller import RatingScalesController
48
  from .api_controller.rating_cata_controller import RatingCataController
49
  from .api_controller.rating_pf_list_controller import RatingPFListController
50
  from .views_controller.tester_list_controller import TesterListController
 
 
16
  from .views_controller.create_session.panel_tags_controller import PanelTagsController
17
  from .views_controller.create_session.panel_codes_controller import PanelCodesController
18
  from .views_controller.create_session.panel_words_controller import PanelWordsController
 
19
 
20
+ from .views_controller.create_session.panels_create.panel_create_escalas_controller import PanelCreateEscalasController
21
+ from .views_controller.create_session.panels_create.panel_create_rata_controller import PanelCreateRataController
22
+ from .views_controller.create_session.panels_create.panel_create_cata_controller import PanelCreateCataController
23
+ from .views_controller.create_session.panels_create.panel_create_pf_controller import PanelCreatePFController
24
+ from .views_controller.create_session.panels_create.panel_create_sort_controller import PanelCreateSortController
25
+ from .views_controller.create_session.panels_create.panel_create_napping_controller import PanelCreateNappingController
26
 
27
+
28
+ from .views_controller.session_management.details.details_controller import DetallesController
29
+ from .views_controller.session_management.details.details_escala_controller import DetallesEscalasController
30
+ from .views_controller.session_management.details.details_rata_controller import DetallesRATAController
31
+ from .views_controller.session_management.details.details_cata_controller import DetallesCATAController
32
+ from .views_controller.session_management.details.details_pf_controller import DetallesPFController
33
+ from .views_controller.session_management.details.details_sort_controller import DetallesSortController
34
+
35
+ from .views_controller.session_management.monitor.monitor_escalas_controller import MonitorEscalasController
36
+ from .views_controller.session_management.monitor.monitor_rata_controller import MonitorRATAController
37
+ from .views_controller.session_management.monitor.monitor_pf_controller import MonitorPFController
38
+ from .views_controller.session_management.monitor.monitor_sort_controller import MonitorSortController
39
 
40
  from .views_controller.sessions_tester.login_session_tester_controller import LoginSessionTesterController
41
  from .views_controller.sessions_tester.list_sessions_tester_controller import ListSessionsTesterController
 
44
  from .views_controller.sessions_tester.tests_forms.test_rata_controller import TestRataController
45
  from .views_controller.sessions_tester.tests_forms.test_cata_controller import TestCataController
46
  from .views_controller.sessions_tester.tests_forms.test_pf_controller import TestPFController
47
+ from .views_controller.sessions_tester.tests_forms.test_sort_controller import TestSortController
48
 
49
  from .views_controller.sessions_tester.init_session.init_session_escalas_controller import InitSessionEscalasController
50
  from .views_controller.sessions_tester.init_session.init_session_rata_controller import InitSessionRATAController
51
  from .views_controller.sessions_tester.init_session.init_session_pf_controller import InitSessionPFController
52
+ from .views_controller.sessions_tester.init_session.init_session_sort_controller import InitSessionSortController
53
 
54
  from .views_controller.vocabulary_manage.create_vocabulary_controller import CreateVocabularyController
55
  from .views_controller.vocabulary_manage.list_vocabulary_controller import ListVocabularyController
 
58
  from .api_controller.rating_cata_controller import RatingCataController
59
  from .api_controller.rating_pf_list_controller import RatingPFListController
60
  from .views_controller.tester_list_controller import TesterListController
61
+ from .api_controller.rating_sort_controller import RatingSortController
tecnicas/controllers/api_controller/rating_sort_controller.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse, HttpRequest
2
+ from django.db import transaction
3
+ from tecnicas.models import Participacion, Palabra, GrupoProducto, Producto
4
+
5
+
6
+ class RatingSortController():
7
+ def __init__(self):
8
+ pass
9
+
10
+ @staticmethod
11
+ def saveRating(request: HttpRequest, data: list[dict]):
12
+ try:
13
+ with transaction.atomic():
14
+ participation = Participacion.objects.get(
15
+ id=request.session["id_participation"])
16
+
17
+ technique = participation.tecnica
18
+ catador = participation.catador
19
+
20
+ # Obtener productos de la técnica
21
+ technique_products = Producto.objects.filter(
22
+ id_tecnica=technique)
23
+ technique_product_ids = set(p.id for p in technique_products)
24
+
25
+ # Recolectar IDs de productos enviados
26
+ sent_product_ids = set()
27
+ for group in data:
28
+ for product in group["products"]:
29
+ sent_product_ids.add(int(product["id"]))
30
+
31
+ # Validar que los productos enviados existan en la técnica
32
+ if not sent_product_ids.issubset(technique_product_ids):
33
+ return JsonResponse({"error": "Productos enviados no pertenecen a la técnica"})
34
+
35
+ # Validar que todos los productos de la técnica estén presentes
36
+ if sent_product_ids != technique_product_ids:
37
+ return JsonResponse({"error": "Faltan productos por clasificar"})
38
+
39
+ for group in data:
40
+ words_data = group["words"]
41
+ products_data = group["products"]
42
+
43
+ # Crear u obtener palabras
44
+ words_objs = []
45
+ for word_name in words_data:
46
+ word, created = Palabra.objects.get_or_create(nombre_palabra=word_name)
47
+ words_objs.append(word)
48
+
49
+ # Crear GrupoProducto
50
+ group_product = GrupoProducto.objects.create(
51
+ tecnica=technique,
52
+ catador=catador
53
+ )
54
+
55
+ # Asignar palabras
56
+ group_product.palabras.set(words_objs)
57
+
58
+ # Asignar productos
59
+ product_ids = [p["id"] for p in products_data]
60
+ group_product.productos.set(product_ids)
61
+
62
+ return JsonResponse({"message": "Valores guardados"})
63
+ except Participacion.DoesNotExist:
64
+ return JsonResponse({"error": "Participación no encontrada"})
65
+ except Exception as e:
66
+ print(f"Error de calificacion: {e}")
67
+ return JsonResponse({"error": "Error al guardar los datos"})
tecnicas/controllers/models_controller/tecnica_controller.py CHANGED
@@ -16,7 +16,7 @@ class TecnicaController():
16
  def setTechniqueFromBasicData(self, basic):
17
  self.technique = Tecnica(
18
  tipo_tecnica=TipoTecnica.objects.get(nombre_tecnica=basic["name_tecnica"]),
19
- id_estilo=EstiloPalabra.objects.get(id=basic["estilo_palabras"]),
20
  repeticiones_max=basic["numero_repeticiones"] or 1,
21
  limite_catadores=basic["numero_catadores"],
22
  instrucciones=basic["instrucciones"] or "Espere instrucciones del Presentador",
 
16
  def setTechniqueFromBasicData(self, basic):
17
  self.technique = Tecnica(
18
  tipo_tecnica=TipoTecnica.objects.get(nombre_tecnica=basic["name_tecnica"]),
19
+ id_estilo=EstiloPalabra.objects.get(nombre_estilo=basic["estilo_palabras"]),
20
  repeticiones_max=basic["numero_repeticiones"] or 1,
21
  limite_catadores=basic["numero_catadores"],
22
  instrucciones=basic["instrucciones"] or "Espere instrucciones del Presentador",
tecnicas/controllers/views_controller/create_session/panel_basic_controller.py CHANGED
@@ -1,4 +1,4 @@
1
- from tecnicas.forms import SesionBasicForm, SesionBasicCATAForm, SesionBasicPFForm
2
  from django.http import HttpRequest
3
  from django.shortcuts import redirect, render
4
  from django.urls import reverse
@@ -10,9 +10,11 @@ class PanelBasicController():
10
  "numero_repeticiones": 1
11
  }
12
 
13
- url_panel_basic = "tecnicas/create_sesion/configuracion-panel-basic.html"
14
  url_panel_basic_cata = "tecnicas/create_sesion/panel-basic-cata.html"
15
  url_panel_basic_pf = "tecnicas/create_sesion/panel-basic-pf.html"
 
 
16
 
17
  url_next_panel_tags = "cata_system:panel_configuracion_tags"
18
  url_next_panel_codes = "cata_system:panel_configuracion_codes"
@@ -43,7 +45,7 @@ class PanelBasicController():
43
  if form.is_valid():
44
  values = {}
45
  for name, value in form.cleaned_data.items():
46
- if name == "estilo_palabras" or name == "tipo_escala":
47
  values[name] = value.id
48
  else:
49
  values[name] = value
@@ -84,7 +86,7 @@ class PanelBasicController():
84
  if form.is_valid():
85
  values = {}
86
  for name, value in form.cleaned_data.items():
87
- if name == "estilo_palabras" or name == "tipo_escala":
88
  values[name] = value.id
89
  else:
90
  values[name] = value
@@ -132,10 +134,7 @@ class PanelBasicController():
132
  if form.is_valid():
133
  values = {}
134
  for name, value in form.cleaned_data.items():
135
- if name == "estilo_palabras":
136
- values[name] = value.id
137
- else:
138
- values[name] = value
139
 
140
  values["name_tecnica"] = name_tecnica
141
  request.session['form_basic'] = values
@@ -164,7 +163,6 @@ class PanelBasicController():
164
  form = SesionBasicPFForm(request.POST)
165
 
166
  if form.is_valid():
167
- print(form.cleaned_data)
168
  values = {}
169
  for name, value in form.cleaned_data.items():
170
  values[name] = value
@@ -178,3 +176,65 @@ class PanelBasicController():
178
  "form_sesion": form, "error": "Información no valida"})
179
 
180
  return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.forms import SesionBasicForm, SesionBasicCATAForm, SesionBasicPFForm, SesionBasicSortForm, SesionBasicNappingForm
2
  from django.http import HttpRequest
3
  from django.shortcuts import redirect, render
4
  from django.urls import reverse
 
10
  "numero_repeticiones": 1
11
  }
12
 
13
+ url_panel_basic = "tecnicas/create_sesion/conf-panel-basic.html"
14
  url_panel_basic_cata = "tecnicas/create_sesion/panel-basic-cata.html"
15
  url_panel_basic_pf = "tecnicas/create_sesion/panel-basic-pf.html"
16
+ url_panel_basic_sort = "tecnicas/create_sesion/panel-basic-sort.html"
17
+ url_panel_basic_napping = "tecnicas/create_sesion/panel-basic-napping.html"
18
 
19
  url_next_panel_tags = "cata_system:panel_configuracion_tags"
20
  url_next_panel_codes = "cata_system:panel_configuracion_codes"
 
45
  if form.is_valid():
46
  values = {}
47
  for name, value in form.cleaned_data.items():
48
+ if name == "tipo_escala":
49
  values[name] = value.id
50
  else:
51
  values[name] = value
 
86
  if form.is_valid():
87
  values = {}
88
  for name, value in form.cleaned_data.items():
89
+ if name == "tipo_escala":
90
  values[name] = value.id
91
  else:
92
  values[name] = value
 
134
  if form.is_valid():
135
  values = {}
136
  for name, value in form.cleaned_data.items():
137
+ values[name] = value
 
 
 
138
 
139
  values["name_tecnica"] = name_tecnica
140
  request.session['form_basic'] = values
 
163
  form = SesionBasicPFForm(request.POST)
164
 
165
  if form.is_valid():
 
166
  values = {}
167
  for name, value in form.cleaned_data.items():
168
  values[name] = value
 
176
  "form_sesion": form, "error": "Información no valida"})
177
 
178
  return response
179
+
180
+ @staticmethod
181
+ def controllGetSort(request: HttpRequest):
182
+ form_sesion = SesionBasicSortForm()
183
+
184
+ view_context = {
185
+ "form_sesion": form_sesion,
186
+ "use_technique": "sort"
187
+ }
188
+
189
+ return render(
190
+ request, PanelBasicController.url_panel_basic_sort, view_context)
191
+
192
+ @staticmethod
193
+ def controllPostSort(request: HttpRequest, name_tecnica: str):
194
+ form = SesionBasicSortForm(request.POST)
195
+
196
+ if form.is_valid():
197
+ values = {}
198
+ for name, value in form.cleaned_data.items():
199
+ values[name] = value
200
+
201
+ values["name_tecnica"] = name_tecnica
202
+ request.session['form_basic'] = values
203
+ response = redirect(
204
+ reverse(PanelBasicController.url_next_panel_codes))
205
+ else:
206
+ response = render(request, PanelBasicController.url_panel_basic_sort, {
207
+ "form_sesion": form, "error": "Información no valida"})
208
+
209
+ return response
210
+
211
+ @staticmethod
212
+ def controllGetNapping(request: HttpRequest):
213
+ form_sesion = SesionBasicNappingForm()
214
+
215
+ view_context = {
216
+ "form_sesion": form_sesion,
217
+ "use_technique": "napping"
218
+ }
219
+
220
+ return render(
221
+ request, PanelBasicController.url_panel_basic_napping, view_context)
222
+
223
+ @staticmethod
224
+ def controllPostNapping(request: HttpRequest, name_tecnica: str):
225
+ form = SesionBasicNappingForm(request.POST)
226
+
227
+ if form.is_valid():
228
+ values = {}
229
+ for name, value in form.cleaned_data.items():
230
+ values[name] = value
231
+
232
+ values["name_tecnica"] = name_tecnica
233
+ request.session['form_basic'] = values
234
+ response = redirect(
235
+ reverse(PanelBasicController.url_next_panel_codes))
236
+ else:
237
+ response = render(request, PanelBasicController.url_panel_basic_napping, {
238
+ "form_sesion": form, "error": "Información no valida"})
239
+
240
+ return response
tecnicas/controllers/views_controller/create_session/panel_codes_controller.py CHANGED
@@ -7,7 +7,7 @@ import json
7
 
8
 
9
  class PanelCodesController():
10
- url_current_panel = "tecnicas/create_sesion/configuracion-panel-codes.html"
11
  url_words = "cata_system:panel_configuracion_words"
12
  url_create_session = "cata_system:creando_sesion"
13
 
@@ -66,7 +66,7 @@ class PanelCodesController():
66
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
67
 
68
  @staticmethod
69
- def controllGetRATA(request: HttpRequest, data, name_technique: str):
70
  num_products = data["numero_productos"]
71
  codes_products = generarCodigos(num_products)
72
  form_codes = CodesForm(codes=codes_products)
@@ -80,7 +80,7 @@ class PanelCodesController():
80
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
81
 
82
  @staticmethod
83
- def controllPostRATA(request: HttpRequest, is_rata=True):
84
  codes = []
85
  context_codes_form = {}
86
 
@@ -92,7 +92,7 @@ class PanelCodesController():
92
 
93
  context_codes_form = {
94
  "form_codes": form_codes,
95
- "use_technique": "rata" if is_rata else "cata"
96
  }
97
 
98
  if form_codes.is_valid():
@@ -117,7 +117,7 @@ class PanelCodesController():
117
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
118
 
119
  @staticmethod
120
- def controllPostPF(request: HttpRequest):
121
  codes = []
122
  context_codes_form = {}
123
 
@@ -129,7 +129,7 @@ class PanelCodesController():
129
 
130
  context_codes_form = {
131
  "form_codes": form_codes,
132
- "use_technique": "perfil flash"
133
  }
134
 
135
  if form_codes.is_valid():
 
7
 
8
 
9
  class PanelCodesController():
10
+ url_current_panel = "tecnicas/create_sesion/conf-panel-codes.html"
11
  url_words = "cata_system:panel_configuracion_words"
12
  url_create_session = "cata_system:creando_sesion"
13
 
 
66
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
67
 
68
  @staticmethod
69
+ def controllGetWithoutOrders(request: HttpRequest, data, name_technique: str):
70
  num_products = data["numero_productos"]
71
  codes_products = generarCodigos(num_products)
72
  form_codes = CodesForm(codes=codes_products)
 
80
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
81
 
82
  @staticmethod
83
+ def controllPostWithWords(request: HttpRequest, name_technique: str):
84
  codes = []
85
  context_codes_form = {}
86
 
 
92
 
93
  context_codes_form = {
94
  "form_codes": form_codes,
95
+ "use_technique": name_technique
96
  }
97
 
98
  if form_codes.is_valid():
 
117
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
118
 
119
  @staticmethod
120
+ def controllPostWithoutOrdersWords(request: HttpRequest, name_technique: str):
121
  codes = []
122
  context_codes_form = {}
123
 
 
129
 
130
  context_codes_form = {
131
  "form_codes": form_codes,
132
+ "use_technique": name_technique
133
  }
134
 
135
  if form_codes.is_valid():
tecnicas/controllers/views_controller/create_session/panel_tags_controller.py CHANGED
@@ -25,7 +25,7 @@ class PanelTagsController():
25
  "form_new_tag": form_new_etiqueta
26
  }
27
 
28
- return render(request, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
29
 
30
  @staticmethod
31
  def controllPostEscalas(request: HttpRequest, data):
@@ -54,7 +54,7 @@ class PanelTagsController():
54
  else:
55
  context_tags["error"] = "ha ocurrido un error"
56
  response = render(
57
- request, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
58
 
59
  return response
60
 
 
25
  "form_new_tag": form_new_etiqueta
26
  }
27
 
28
+ return render(request, "tecnicas/create_sesion/conf-panel-tags.html", context_tags)
29
 
30
  @staticmethod
31
  def controllPostEscalas(request: HttpRequest, data):
 
54
  else:
55
  context_tags["error"] = "ha ocurrido un error"
56
  response = render(
57
+ request, "tecnicas/create_sesion/conf-panel-tags.html", context_tags)
58
 
59
  return response
60
 
tecnicas/controllers/views_controller/create_session/panel_words_controller.py CHANGED
@@ -7,7 +7,7 @@ import json
7
 
8
 
9
  class PanelWordsController():
10
- current_url_escalas_atribute = "tecnicas/create_sesion/configuracion-panel-words.html"
11
  current_url_escalas_vocabulary = "tecnicas/create_sesion/conf-panel-vocabulary.html"
12
 
13
  def __init__(self):
 
7
 
8
 
9
  class PanelWordsController():
10
+ current_url_escalas_atribute = "tecnicas/create_sesion/conf-panel-words.html"
11
  current_url_escalas_vocabulary = "tecnicas/create_sesion/conf-panel-vocabulary.html"
12
 
13
  def __init__(self):
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_cata_controller.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.models import EsVocabulario, Tecnica, TipoTecnica, EstiloPalabra, EsAtributo, Vocabulario, Palabra, SesionSensorial, Producto
5
+ from tecnicas.utils import deleteDataSession, general_error
6
+
7
+
8
+ class PanelCreateCataController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_codes") or not request.session.get("form_words"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # //////////////////////////// #
21
+ #
22
+ # First step: Create technique #
23
+ #
24
+ # //////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ data_basic["numero_catadores"] = 0
27
+ data_basic["numero_repeticiones"] = 1
28
+
29
+ technique = Tecnica.objects.create(
30
+ tipo_tecnica=TipoTecnica.objects.get(
31
+ nombre_tecnica=data_basic["name_tecnica"]),
32
+ id_estilo=EstiloPalabra.objects.get(
33
+ nombre_estilo=data_basic["estilo_palabras"]),
34
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ # ////////////////////////////////////////////// #
43
+ #
44
+ # Second step: Create productos with their codes #
45
+ #
46
+ # ////////////////////////////////////////////// #
47
+ codes = request.session["form_codes"]
48
+
49
+ if not codes:
50
+ raise ValueError("No hay códigos de productos")
51
+
52
+ products_without_save = []
53
+ for code in codes:
54
+ product = Producto(
55
+ codigoProducto=code,
56
+ id_tecnica=technique
57
+ )
58
+ products_without_save.append(product)
59
+
60
+ Producto.objects.bulk_create(products_without_save)
61
+
62
+ # /////////////////////////////////////////////////////// #
63
+ #
64
+ # Third step: Create relations technique with Words Style #
65
+ #
66
+ # /////////////////////////////////////////////////////// #
67
+ style_words = technique.id_estilo.nombre_estilo
68
+
69
+ if style_words == "atributos":
70
+ raw_ids_words = request.session["form_words"]
71
+ ids_words = [int(id_w) for id_w in raw_ids_words]
72
+
73
+ words = Palabra.objects.filter(id__in=ids_words)
74
+
75
+ style_atribute = EsAtributo.objects.create(
76
+ id_tecnica=technique
77
+ )
78
+
79
+ if not style_atribute:
80
+ raise ValueError(
81
+ "Error al intentar relacionar las palabras con la técnica")
82
+
83
+ style_atribute.palabras.set(words)
84
+
85
+ elif style_words == "vocabulario":
86
+ name_vocabulary = request.session["form_words"]
87
+ try:
88
+ vocabulary = Vocabulario.objects.get(
89
+ nombre_vocabulario=name_vocabulary)
90
+ except Vocabulario.DoesNotExist:
91
+ raise ValueError("Vocabulario no encontrado")
92
+
93
+ es_vocabulary = EsVocabulario.objects.create(
94
+ id_tecnica=technique,
95
+ id_vocabulario=vocabulary
96
+ )
97
+ if not es_vocabulary:
98
+ raise ValueError(
99
+ "Error al intentar relacionar el vocabulario con la técnica")
100
+
101
+ else:
102
+ raise ValueError("Estilo de palabas no permitido")
103
+
104
+ # //////////////////////////////////////////////////////// #
105
+ #
106
+ # Fourth step: Create session and relat with the technique #
107
+ #
108
+ # //////////////////////////////////////////////////////// #
109
+ session = SesionSensorial.objects.create(
110
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
111
+ tecnica=technique,
112
+ creadoPor=request.user.user_presentador
113
+ )
114
+
115
+ if not session:
116
+ raise ValueError("Error al crear sesion sensorial")
117
+
118
+ context = {
119
+ "message": "sesión creada",
120
+ "data": {
121
+ "codigo_sesion": session.codigo_sesion,
122
+ "nombre_sesion": session.nombre_sesion
123
+ }
124
+ }
125
+
126
+ # ////////////////////////////////// #
127
+ #
128
+ # Final step: Delete date en session #
129
+ #
130
+ # ////////////////////////////////// #
131
+ deleteDataSession(request)
132
+ return JsonResponse(context)
133
+
134
+ except ValueError as e:
135
+ return general_error(f"Error: {e}")
136
+ else:
137
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_controller.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse, HttpRequest
2
+ from django.shortcuts import render
3
+
4
+ class PanelCreateController():
5
+ url_template = 'tecnicas/create_sesion/creating_session.html'
6
+
7
+ def __init__(self):
8
+ pass
9
+
10
+ @staticmethod
11
+ def controllGet(request: HttpRequest):
12
+ return render(
13
+ request, PanelCreateController.url_template)
14
+
15
+ @staticmethod
16
+ def controllPost(request: HttpRequest):
17
+ return JsonResponse({"message": "Método no permitido"})
tecnicas/controllers/views_controller/create_session/{panel_create_controller.py → panels_create/panel_create_escalas_controller.py} RENAMED
@@ -1,23 +1,17 @@
 
1
  from django.http import HttpRequest, JsonResponse
2
  from django.db import transaction
3
- from django.shortcuts import render
4
- from tecnicas.utils import general_error
5
- from tecnicas.models import EsAtributo, EsVocabulario, Vocabulario, Tecnica, TipoTecnica, EstiloPalabra, Producto, Palabra, SesionSensorial, Escala, TipoEscala, EtiquetasEscala
6
- from tecnicas.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, EstiloPalabrasController, PalabrasController, SesionController
7
- from tecnicas.utils import deleteDataSession
8
 
9
 
10
- class PanelCreateController():
11
  def __init__(self):
12
- pass
13
 
14
  @staticmethod
15
- def controllGetEscalas(request: HttpRequest):
16
- return render(
17
- request, 'tecnicas/create_sesion/creando_sesion.html')
18
-
19
- @staticmethod
20
- def controllPostEscalas(request: HttpRequest):
21
  if request.POST.get('action') == 'create_session':
22
  if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
23
  deleteDataSession(request)
@@ -173,368 +167,3 @@ class PanelCreateController():
173
  return general_error(f"Error: {e}")
174
  else:
175
  return general_error("No se ha establecido acción")
176
-
177
- @staticmethod
178
- def controllPostRATA(request: HttpRequest):
179
- if request.POST.get('action') == 'create_session':
180
- if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
181
- deleteDataSession(request)
182
- return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
183
- try:
184
- with transaction.atomic():
185
- # ////////////////////////////////////////////////////// #
186
- #
187
- # First step: Create technique and scale with their tags #
188
- #
189
- # ////////////////////////////////////////////////////// #
190
- data_basic = request.session["form_basic"]
191
- data_basic["numero_catadores"] = 0
192
- data_basic["numero_repeticiones"] = 1
193
-
194
- technique = Tecnica.objects.create(
195
- tipo_tecnica=TipoTecnica.objects.get(
196
- nombre_tecnica=data_basic["name_tecnica"]),
197
- id_estilo=EstiloPalabra.objects.get(
198
- id=data_basic["estilo_palabras"]),
199
- repeticiones_max=data_basic["numero_repeticiones"] or 1,
200
- limite_catadores=data_basic["numero_catadores"],
201
- instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
202
- )
203
-
204
- if not technique:
205
- raise ValueError("Error al guardar la técnica")
206
-
207
- data_scale = {
208
- "id_scale": data_basic["tipo_escala"],
209
- "size": data_basic["tamano_escala"],
210
- "technique": technique
211
- }
212
-
213
- controllerScale = EscalaController(data=data_scale)
214
-
215
- scale = controllerScale.saveScale()
216
- if isinstance(scale, dict):
217
- raise ValueError(scale["error"])
218
-
219
- dict_tags = request.session["form_tags"]
220
- saved_related_tags = controllerScale.realteTags(dict_tags)
221
- if "error" in saved_related_tags:
222
- raise ValueError(saved_related_tags["error"])
223
-
224
- # ////////////////////////////////////////////// #
225
- #
226
- # Second step: Create productos with their codes #
227
- #
228
- # ////////////////////////////////////////////// #
229
- codes = request.session["form_codes"]
230
-
231
- if not codes:
232
- raise ValueError("No hay códigos de productos")
233
-
234
- products_without_save = []
235
- for code in codes:
236
- product = Producto(
237
- codigoProducto=code,
238
- id_tecnica=technique
239
- )
240
- products_without_save.append(product)
241
-
242
- Producto.objects.bulk_create(products_without_save)
243
-
244
- # /////////////////////////////////////////////////////// #
245
- #
246
- # Third step: Create relations technique with Words Style #
247
- #
248
- # /////////////////////////////////////////////////////// #
249
- style_words = technique.id_estilo.nombre_estilo
250
-
251
- if style_words == "atributos":
252
- raw_ids_words = request.session["form_words"]
253
- ids_words = [int(id_w) for id_w in raw_ids_words]
254
-
255
- words = Palabra.objects.filter(id__in=ids_words)
256
-
257
- style_atribute = EsAtributo.objects.create(
258
- id_tecnica=technique
259
- )
260
-
261
- if not style_atribute:
262
- raise ValueError(
263
- "Error al intentar relacionar las palabras con la técnica")
264
-
265
- style_atribute.palabras.set(words)
266
-
267
- elif style_words == "vocabulario":
268
- name_vocabulary = request.session["form_words"]
269
- try:
270
- vocabulary = Vocabulario.objects.get(
271
- nombre_vocabulario=name_vocabulary)
272
- except Vocabulario.DoesNotExist:
273
- raise ValueError("Vocabulario no encontrado")
274
-
275
- es_vocabulary = EsVocabulario.objects.create(
276
- id_tecnica=technique,
277
- id_vocabulario=vocabulary
278
- )
279
- if not es_vocabulary:
280
- raise ValueError(
281
- "Error al intentar relacionar el vocabulario con la técnica")
282
-
283
- else:
284
- raise ValueError("Estilo de palabas no permitido")
285
-
286
- # //////////////////////////////////////////////////////// #
287
- #
288
- # Fourth step: Create session and relat with the technique #
289
- #
290
- # //////////////////////////////////////////////////////// #
291
- session = SesionSensorial.objects.create(
292
- nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else "",
293
- tecnica=technique,
294
- creadoPor=request.user.user_presentador
295
- )
296
-
297
- if not session:
298
- raise ValueError("Error al crear sesion sensorial")
299
-
300
- context = {
301
- "message": "sesión creada",
302
- "data": {
303
- "codigo_sesion": session.codigo_sesion,
304
- "nombre_sesion": session.nombre_sesion
305
- }
306
- }
307
-
308
- # ////////////////////////////////// #
309
- #
310
- # Final step: Delete date en session #
311
- #
312
- # ////////////////////////////////// #
313
-
314
- deleteDataSession(request)
315
- return JsonResponse(context)
316
- except ValueError as e:
317
- return general_error(f"Error: {e}")
318
- else:
319
- return general_error("No se ha establecido acción")
320
-
321
- @staticmethod
322
- def controllPostCATA(request: HttpRequest):
323
- if request.POST.get('action') == 'create_session':
324
- if not request.session.get("form_codes") or not request.session.get("form_words"):
325
- deleteDataSession(request)
326
- return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
327
- try:
328
- with transaction.atomic():
329
- # //////////////////////////// #
330
- #
331
- # First step: Create technique #
332
- #
333
- # //////////////////////////// #
334
- data_basic = request.session["form_basic"]
335
- data_basic["numero_catadores"] = 0
336
- data_basic["numero_repeticiones"] = 1
337
-
338
- technique = Tecnica.objects.create(
339
- tipo_tecnica=TipoTecnica.objects.get(
340
- nombre_tecnica=data_basic["name_tecnica"]),
341
- id_estilo=EstiloPalabra.objects.get(
342
- id=data_basic["estilo_palabras"]),
343
- repeticiones_max=data_basic["numero_repeticiones"] or 1,
344
- limite_catadores=data_basic["numero_catadores"],
345
- instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
346
- )
347
-
348
- if not technique:
349
- raise ValueError("Error al guardar la técnica")
350
-
351
- # ////////////////////////////////////////////// #
352
- #
353
- # Second step: Create productos with their codes #
354
- #
355
- # ////////////////////////////////////////////// #
356
- codes = request.session["form_codes"]
357
-
358
- if not codes:
359
- raise ValueError("No hay códigos de productos")
360
-
361
- products_without_save = []
362
- for code in codes:
363
- product = Producto(
364
- codigoProducto=code,
365
- id_tecnica=technique
366
- )
367
- products_without_save.append(product)
368
-
369
- Producto.objects.bulk_create(products_without_save)
370
-
371
- # /////////////////////////////////////////////////////// #
372
- #
373
- # Third step: Create relations technique with Words Style #
374
- #
375
- # /////////////////////////////////////////////////////// #
376
- style_words = technique.id_estilo.nombre_estilo
377
-
378
- if style_words == "atributos":
379
- raw_ids_words = request.session["form_words"]
380
- ids_words = [int(id_w) for id_w in raw_ids_words]
381
-
382
- words = Palabra.objects.filter(id__in=ids_words)
383
-
384
- style_atribute = EsAtributo.objects.create(
385
- id_tecnica=technique
386
- )
387
-
388
- if not style_atribute:
389
- raise ValueError(
390
- "Error al intentar relacionar las palabras con la técnica")
391
-
392
- style_atribute.palabras.set(words)
393
-
394
- elif style_words == "vocabulario":
395
- name_vocabulary = request.session["form_words"]
396
- try:
397
- vocabulary = Vocabulario.objects.get(
398
- nombre_vocabulario=name_vocabulary)
399
- except Vocabulario.DoesNotExist:
400
- raise ValueError("Vocabulario no encontrado")
401
-
402
- es_vocabulary = EsVocabulario.objects.create(
403
- id_tecnica=technique,
404
- id_vocabulario=vocabulary
405
- )
406
- if not es_vocabulary:
407
- raise ValueError(
408
- "Error al intentar relacionar el vocabulario con la técnica")
409
-
410
- else:
411
- raise ValueError("Estilo de palabas no permitido")
412
-
413
- # //////////////////////////////////////////////////////// #
414
- #
415
- # Fourth step: Create session and relat with the technique #
416
- #
417
- # //////////////////////////////////////////////////////// #
418
- session = SesionSensorial.objects.create(
419
- nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
420
- tecnica=technique,
421
- creadoPor=request.user.user_presentador
422
- )
423
-
424
- if not session:
425
- raise ValueError("Error al crear sesion sensorial")
426
-
427
- context = {
428
- "message": "sesión creada",
429
- "data": {
430
- "codigo_sesion": session.codigo_sesion,
431
- "nombre_sesion": session.nombre_sesion
432
- }
433
- }
434
-
435
- # ////////////////////////////////// #
436
- #
437
- # Final step: Delete date en session #
438
- #
439
- # ////////////////////////////////// #
440
- deleteDataSession(request)
441
- return JsonResponse(context)
442
-
443
- except ValueError as e:
444
- return general_error(f"Error: {e}")
445
- else:
446
- return general_error("No se ha establecido acción")
447
-
448
- @staticmethod
449
- def controllPostPF(request: HttpRequest):
450
- if request.POST.get('action') == 'create_session':
451
- if not request.session.get("form_codes"):
452
- deleteDataSession(request)
453
- return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
454
- try:
455
- with transaction.atomic():
456
- # ////////////////////////////////////// #
457
- #
458
- # First step: Create technique and scale #
459
- #
460
- # ////////////////////////////////////// #
461
- data_basic = request.session["form_basic"]
462
- phases_before_reptition = 2
463
-
464
- technique = Tecnica.objects.create(
465
- tipo_tecnica=TipoTecnica.objects.get(
466
- nombre_tecnica=data_basic["name_tecnica"]),
467
- id_estilo=EstiloPalabra.objects.get(
468
- nombre_estilo="vocabulario"),
469
- repeticiones_max=data_basic["numero_repeticiones"] + phases_before_reptition,
470
- limite_catadores=data_basic["numero_catadores"],
471
- instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Analista",
472
- )
473
-
474
- if not technique:
475
- raise ValueError("Error al guardar la técnica")
476
-
477
- created_scale = Escala.objects.create(
478
- id_tipo_escala=TipoEscala.objects.get(
479
- nombre_escala="estructurada"),
480
- longitud=data_basic["numero_productos"],
481
- tecnica=technique
482
- )
483
-
484
- if not created_scale:
485
- raise ValueError("No se ha podido crear la escala")
486
-
487
- # ////////////////////////////////////////////// #
488
- #
489
- # Second step: Create productos with their codes #
490
- #
491
- # ////////////////////////////////////////////// #
492
- codes = request.session["form_codes"]
493
-
494
- if not codes:
495
- raise ValueError("No hay códigos de productos")
496
-
497
- products_without_save = []
498
- for code in codes:
499
- product = Producto(
500
- codigoProducto=code,
501
- id_tecnica=technique
502
- )
503
- products_without_save.append(product)
504
-
505
- Producto.objects.bulk_create(products_without_save)
506
-
507
- # /////////////////////////////////////////////////////// #
508
- #
509
- # Third step: Create session and relat with the technique #
510
- #
511
- # /////////////////////////////////////////////////////// #
512
- session = SesionSensorial.objects.create(
513
- nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
514
- tecnica=technique,
515
- creadoPor=request.user.user_presentador
516
- )
517
-
518
- if not session:
519
- raise ValueError("Error al crear sesion sensorial")
520
-
521
- context = {
522
- "message": "sesión creada",
523
- "data": {
524
- "codigo_sesion": session.codigo_sesion,
525
- "nombre_sesion": session.nombre_sesion
526
- }
527
- }
528
-
529
- # ////////////////////////////////// #
530
- #
531
- # Final step: Delete date en session #
532
- #
533
- # ////////////////////////////////// #
534
- deleteDataSession(request)
535
- return JsonResponse(context)
536
-
537
- except ValueError as e:
538
- return general_error(f"Error: {e}")
539
- else:
540
- return general_error("No se ha establecido acción")
 
1
+ from .panel_create_controller import PanelCreateController
2
  from django.http import HttpRequest, JsonResponse
3
  from django.db import transaction
4
+ from tecnicas.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, PalabrasController, EstiloPalabrasController, SesionController
5
+ from tecnicas.models import EsVocabulario, Vocabulario
6
+ from tecnicas.utils import deleteDataSession, general_error
 
 
7
 
8
 
9
+ class PanelCreateEscalasController(PanelCreateController):
10
  def __init__(self):
11
+ super().__init__()
12
 
13
  @staticmethod
14
+ def controllPost(request: HttpRequest):
 
 
 
 
 
15
  if request.POST.get('action') == 'create_session':
16
  if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
17
  deleteDataSession(request)
 
167
  return general_error(f"Error: {e}")
168
  else:
169
  return general_error("No se ha establecido acción")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_napping_controller.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from tecnicas.models import Tecnica, TipoTecnica, EstiloPalabra, Producto, SesionSensorial
4
+ from django.db import transaction
5
+ from tecnicas.utils import deleteDataSession
6
+
7
+
8
+ class PanelCreateNappingController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_basic") or not request.session.get("form_codes"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # //////////////////////////// #
21
+ #
22
+ # First step: Create technique #
23
+ #
24
+ # //////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ data_basic["numero_catadores"] = data_basic["numero_catadores"] or 2
27
+ data_basic["numero_repeticiones"] = 0
28
+
29
+ technique = Tecnica.objects.create(
30
+ tipo_tecnica=TipoTecnica.objects.get(
31
+ nombre_tecnica=data_basic["name_tecnica"]),
32
+ id_estilo=EstiloPalabra.objects.get(
33
+ nombre_estilo="napping"),
34
+ repeticiones_max=data_basic["numero_repeticiones"],
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ # ////////////////////////////////////////////// #
43
+ #
44
+ # Second step: Create productos with their codes #
45
+ #
46
+ # ////////////////////////////////////////////// #
47
+ codes = request.session["form_codes"]
48
+
49
+ if not codes:
50
+ raise ValueError("No hay códigos de productos")
51
+
52
+ products_without_save = []
53
+ for code in codes:
54
+ product = Producto(
55
+ codigoProducto=code,
56
+ id_tecnica=technique
57
+ )
58
+ products_without_save.append(product)
59
+
60
+ Producto.objects.bulk_create(products_without_save)
61
+
62
+ # /////////////////////////////////////////////////////// #
63
+ #
64
+ # Third step: Create session and relat with the technique #
65
+ #
66
+ # /////////////////////////////////////////////////////// #
67
+ session = SesionSensorial.objects.create(
68
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
69
+ tecnica=technique,
70
+ creadoPor=request.user.user_presentador
71
+ )
72
+
73
+ if not session:
74
+ raise ValueError("Error al crear sesion sensorial")
75
+
76
+ context = {
77
+ "message": "sesión creada",
78
+ "data": {
79
+ "codigo_sesion": session.codigo_sesion,
80
+ "nombre_sesion": session.nombre_sesion
81
+ }
82
+ }
83
+
84
+ # ////////////////////////////////// #
85
+ #
86
+ # Final step: Delete date en session #
87
+ #
88
+ # ////////////////////////////////// #
89
+ deleteDataSession(request)
90
+ return JsonResponse(context)
91
+
92
+ except ValueError as e:
93
+ return general_error(f"Error: {e}")
94
+ else:
95
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_pf_controller.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.models import Tecnica, TipoTecnica, EstiloPalabra, SesionSensorial, Escala, TipoEscala, Producto
5
+ from tecnicas.utils import deleteDataSession, general_error
6
+
7
+
8
+ class PanelCreatePFController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_codes"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # ////////////////////////////////////// #
21
+ #
22
+ # First step: Create technique and scale #
23
+ #
24
+ # ////////////////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ phases_before_reptition = 2
27
+
28
+ technique = Tecnica.objects.create(
29
+ tipo_tecnica=TipoTecnica.objects.get(
30
+ nombre_tecnica=data_basic["name_tecnica"]),
31
+ id_estilo=EstiloPalabra.objects.get(
32
+ nombre_estilo="perfil flash"),
33
+ repeticiones_max=data_basic["numero_repeticiones"] +
34
+ phases_before_reptition,
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Analista",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ created_scale = Escala.objects.create(
43
+ id_tipo_escala=TipoEscala.objects.get(
44
+ nombre_escala="estructurada"),
45
+ longitud=data_basic["numero_productos"],
46
+ tecnica=technique
47
+ )
48
+
49
+ if not created_scale:
50
+ raise ValueError("No se ha podido crear la escala")
51
+
52
+ # ////////////////////////////////////////////// #
53
+ #
54
+ # Second step: Create productos with their codes #
55
+ #
56
+ # ////////////////////////////////////////////// #
57
+ codes = request.session["form_codes"]
58
+
59
+ if not codes:
60
+ raise ValueError("No hay códigos de productos")
61
+
62
+ products_without_save = []
63
+ for code in codes:
64
+ product = Producto(
65
+ codigoProducto=code,
66
+ id_tecnica=technique
67
+ )
68
+ products_without_save.append(product)
69
+
70
+ Producto.objects.bulk_create(products_without_save)
71
+
72
+ # /////////////////////////////////////////////////////// #
73
+ #
74
+ # Third step: Create session and relat with the technique #
75
+ #
76
+ # /////////////////////////////////////////////////////// #
77
+ session = SesionSensorial.objects.create(
78
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
79
+ tecnica=technique,
80
+ creadoPor=request.user.user_presentador
81
+ )
82
+
83
+ if not session:
84
+ raise ValueError("Error al crear sesion sensorial")
85
+
86
+ context = {
87
+ "message": "sesión creada",
88
+ "data": {
89
+ "codigo_sesion": session.codigo_sesion,
90
+ "nombre_sesion": session.nombre_sesion
91
+ }
92
+ }
93
+
94
+ # ////////////////////////////////// #
95
+ #
96
+ # Final step: Delete date en session #
97
+ #
98
+ # ////////////////////////////////// #
99
+ deleteDataSession(request)
100
+ return JsonResponse(context)
101
+
102
+ except ValueError as e:
103
+ return general_error(f"Error: {e}")
104
+ else:
105
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_rata_controller.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.controllers import EscalaController
5
+ from tecnicas.models import EsVocabulario, Tecnica, TipoTecnica, EstiloPalabra, EsAtributo, Vocabulario, Palabra, SesionSensorial, Producto
6
+ from tecnicas.utils import deleteDataSession, general_error
7
+
8
+
9
+ class PanelCreateRataController(PanelCreateController):
10
+ def __init__(self):
11
+ super().__init__()
12
+
13
+ @staticmethod
14
+ def controllPost(request: HttpRequest):
15
+ if request.POST.get('action') == 'create_session':
16
+ if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
17
+ deleteDataSession(request)
18
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
19
+ try:
20
+ with transaction.atomic():
21
+ # ////////////////////////////////////////////////////// #
22
+ #
23
+ # First step: Create technique and scale with their tags #
24
+ #
25
+ # ////////////////////////////////////////////////////// #
26
+ data_basic = request.session["form_basic"]
27
+ data_basic["numero_catadores"] = 0
28
+ data_basic["numero_repeticiones"] = 1
29
+
30
+ technique = Tecnica.objects.create(
31
+ tipo_tecnica=TipoTecnica.objects.get(
32
+ nombre_tecnica=data_basic["name_tecnica"]),
33
+ id_estilo=EstiloPalabra.objects.get(
34
+ nombre_estilo=data_basic["estilo_palabras"]),
35
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
36
+ limite_catadores=data_basic["numero_catadores"],
37
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
38
+ )
39
+
40
+ if not technique:
41
+ raise ValueError("Error al guardar la técnica")
42
+
43
+ data_scale = {
44
+ "id_scale": data_basic["tipo_escala"],
45
+ "size": data_basic["tamano_escala"],
46
+ "technique": technique
47
+ }
48
+
49
+ controllerScale = EscalaController(data=data_scale)
50
+
51
+ scale = controllerScale.saveScale()
52
+ if isinstance(scale, dict):
53
+ raise ValueError(scale["error"])
54
+
55
+ dict_tags = request.session["form_tags"]
56
+ saved_related_tags = controllerScale.realteTags(dict_tags)
57
+ if "error" in saved_related_tags:
58
+ raise ValueError(saved_related_tags["error"])
59
+
60
+ # ////////////////////////////////////////////// #
61
+ #
62
+ # Second step: Create productos with their codes #
63
+ #
64
+ # ////////////////////////////////////////////// #
65
+ codes = request.session["form_codes"]
66
+
67
+ if not codes:
68
+ raise ValueError("No hay códigos de productos")
69
+
70
+ products_without_save = []
71
+ for code in codes:
72
+ product = Producto(
73
+ codigoProducto=code,
74
+ id_tecnica=technique
75
+ )
76
+ products_without_save.append(product)
77
+
78
+ Producto.objects.bulk_create(products_without_save)
79
+
80
+ # /////////////////////////////////////////////////////// #
81
+ #
82
+ # Third step: Create relations technique with Words Style #
83
+ #
84
+ # /////////////////////////////////////////////////////// #
85
+ style_words = technique.id_estilo.nombre_estilo
86
+
87
+ if style_words == "atributos":
88
+ raw_ids_words = request.session["form_words"]
89
+ ids_words = [int(id_w) for id_w in raw_ids_words]
90
+
91
+ words = Palabra.objects.filter(id__in=ids_words)
92
+
93
+ style_atribute = EsAtributo.objects.create(
94
+ id_tecnica=technique
95
+ )
96
+
97
+ if not style_atribute:
98
+ raise ValueError(
99
+ "Error al intentar relacionar las palabras con la técnica")
100
+
101
+ style_atribute.palabras.set(words)
102
+
103
+ elif style_words == "vocabulario":
104
+ name_vocabulary = request.session["form_words"]
105
+ try:
106
+ vocabulary = Vocabulario.objects.get(
107
+ nombre_vocabulario=name_vocabulary)
108
+ except Vocabulario.DoesNotExist:
109
+ raise ValueError("Vocabulario no encontrado")
110
+
111
+ es_vocabulary = EsVocabulario.objects.create(
112
+ id_tecnica=technique,
113
+ id_vocabulario=vocabulary
114
+ )
115
+ if not es_vocabulary:
116
+ raise ValueError(
117
+ "Error al intentar relacionar el vocabulario con la técnica")
118
+
119
+ else:
120
+ raise ValueError("Estilo de palabas no permitido")
121
+
122
+ # //////////////////////////////////////////////////////// #
123
+ #
124
+ # Fourth step: Create session and relat with the technique #
125
+ #
126
+ # //////////////////////////////////////////////////////// #
127
+ session = SesionSensorial.objects.create(
128
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else "",
129
+ tecnica=technique,
130
+ creadoPor=request.user.user_presentador
131
+ )
132
+
133
+ if not session:
134
+ raise ValueError("Error al crear sesion sensorial")
135
+
136
+ context = {
137
+ "message": "sesión creada",
138
+ "data": {
139
+ "codigo_sesion": session.codigo_sesion,
140
+ "nombre_sesion": session.nombre_sesion
141
+ }
142
+ }
143
+
144
+ # ////////////////////////////////// #
145
+ #
146
+ # Final step: Delete date en session #
147
+ #
148
+ # ////////////////////////////////// #
149
+
150
+ deleteDataSession(request)
151
+ return JsonResponse(context)
152
+ except ValueError as e:
153
+ return general_error(f"Error: {e}")
154
+ else:
155
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_sort_controller.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.models import Tecnica, TipoTecnica, EstiloPalabra, SesionSensorial, Producto
5
+ from tecnicas.utils import deleteDataSession, general_error
6
+
7
+
8
+ class PanelCreateSortController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_basic") or not request.session.get("form_codes"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # //////////////////////////// #
21
+ #
22
+ # First step: Create technique #
23
+ #
24
+ # //////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ data_basic["numero_catadores"] = data_basic["numero_catadores"] or 1
27
+ data_basic["numero_repeticiones"] = 1
28
+
29
+ technique = Tecnica.objects.create(
30
+ tipo_tecnica=TipoTecnica.objects.get(
31
+ nombre_tecnica=data_basic["name_tecnica"]),
32
+ id_estilo=EstiloPalabra.objects.get(
33
+ nombre_estilo="sort"),
34
+ repeticiones_max=data_basic["numero_repeticiones"],
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ # ////////////////////////////////////////////// #
43
+ #
44
+ # Second step: Create productos with their codes #
45
+ #
46
+ # ////////////////////////////////////////////// #
47
+ codes = request.session["form_codes"]
48
+
49
+ if not codes:
50
+ raise ValueError("No hay códigos de productos")
51
+
52
+ products_without_save = []
53
+ for code in codes:
54
+ product = Producto(
55
+ codigoProducto=code,
56
+ id_tecnica=technique
57
+ )
58
+ products_without_save.append(product)
59
+
60
+ Producto.objects.bulk_create(products_without_save)
61
+
62
+ # /////////////////////////////////////////////////////// #
63
+ #
64
+ # Third step: Create session and relat with the technique #
65
+ #
66
+ # /////////////////////////////////////////////////////// #
67
+ session = SesionSensorial.objects.create(
68
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
69
+ tecnica=technique,
70
+ creadoPor=request.user.user_presentador
71
+ )
72
+
73
+ if not session:
74
+ raise ValueError("Error al crear sesion sensorial")
75
+
76
+ context = {
77
+ "message": "sesión creada",
78
+ "data": {
79
+ "codigo_sesion": session.codigo_sesion,
80
+ "nombre_sesion": session.nombre_sesion
81
+ }
82
+ }
83
+
84
+ # ////////////////////////////////// #
85
+ #
86
+ # Final step: Delete date en session #
87
+ #
88
+ # ////////////////////////////////// #
89
+ deleteDataSession(request)
90
+ return JsonResponse(context)
91
+
92
+ except ValueError as e:
93
+ return general_error(f"Error: {e}")
94
+ else:
95
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/session_management/{details_cata_controller.py → details/details_cata_controller.py} RENAMED
@@ -9,7 +9,7 @@ from collections import defaultdict
9
  class DetallesCATAController(DetallesController):
10
  def __init__(self, session: SesionSensorial):
11
  super().__init__(session)
12
- self.url_template = "tecnicas/manage_sesions/detalles-sesion-cata.html"
13
  self.url_next = "cata_system:monitor_sesion"
14
 
15
  def getContext(self):
 
9
  class DetallesCATAController(DetallesController):
10
  def __init__(self, session: SesionSensorial):
11
  super().__init__(session)
12
+ self.url_template = "tecnicas/manage_sesions/details-session-cata.html"
13
  self.url_next = "cata_system:monitor_sesion"
14
 
15
  def getContext(self):
tecnicas/controllers/views_controller/session_management/{details_controller.py → details/details_controller.py} RENAMED
@@ -7,7 +7,7 @@ from tecnicas.controllers import ParticipacionController
7
 
8
  class DetallesController():
9
  url_template: str
10
- url_next: str
11
 
12
  def __init__(self, session: SesionSensorial):
13
  self.session = session
 
7
 
8
  class DetallesController():
9
  url_template: str
10
+ url_next = "cata_system:monitor_sesion"
11
 
12
  def __init__(self, session: SesionSensorial):
13
  self.session = session
tecnicas/controllers/views_controller/session_management/{details_escala_controller.py → details/details_escala_controller.py} RENAMED
@@ -11,20 +11,17 @@ Encabezados de como deben de aparecer los datos juntos
11
  | Repeticion | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
12
 
13
  '''
14
- from django.http import HttpRequest
15
- from django.shortcuts import render, redirect
16
- from django.urls import reverse
17
- from tecnicas.models import SesionSensorial, Presentador, Participacion, Calificacion, Escala
18
- from tecnicas.controllers import DatoController, PalabrasController, ParticipacionController
19
  from .details_controller import DetallesController
20
- from tecnicas.utils import defaultdict_to_dict, controller_error
21
  from collections import defaultdict
22
 
23
 
24
  class DetallesEscalasController(DetallesController):
25
  def __init__(self, session: SesionSensorial):
26
  super().__init__(session)
27
- self.url_template = "tecnicas/manage_sesions/detalles-sesion.html"
28
  self.url_next = "cata_system:monitor_sesion"
29
 
30
  def getContext(self):
 
11
  | Repeticion | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
12
 
13
  '''
14
+ from tecnicas.models import SesionSensorial, Calificacion, Escala
15
+ from tecnicas.controllers import DatoController, PalabrasController
 
 
 
16
  from .details_controller import DetallesController
17
+ from tecnicas.utils import defaultdict_to_dict
18
  from collections import defaultdict
19
 
20
 
21
  class DetallesEscalasController(DetallesController):
22
  def __init__(self, session: SesionSensorial):
23
  super().__init__(session)
24
+ self.url_template = "tecnicas/manage_sesions/details-session.html"
25
  self.url_next = "cata_system:monitor_sesion"
26
 
27
  def getContext(self):
tecnicas/controllers/views_controller/session_management/{details_pf_controller.py → details/details_pf_controller.py} RENAMED
@@ -1,8 +1,5 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import redirect
3
- from django.urls import reverse
4
- from tecnicas.models import SesionSensorial, Presentador, Participacion, ListaPalabras, Calificacion, Catador
5
- from tecnicas.controllers import ParticipacionController, DatoController
6
  from tecnicas.utils import defaultdict_to_dict
7
  from .details_controller import DetallesController
8
  from collections import defaultdict
@@ -20,9 +17,7 @@ class DetallesPFController(DetallesController):
20
  self.context = {
21
  "sesion": self.session,
22
  "use_technique": technique,
23
- "existen_calificaciones": False,
24
  "tipo_escala": technique.escala_tecnica.id_tipo_escala.nombre_escala,
25
- "valor_max": technique.escala_tecnica.longitud,
26
  "repeticiones_max": technique.repeticiones_max - 2
27
  }
28
 
 
1
+ from tecnicas.models import SesionSensorial, ListaPalabras, Calificacion, Catador
2
+ from tecnicas.controllers import DatoController
 
 
 
3
  from tecnicas.utils import defaultdict_to_dict
4
  from .details_controller import DetallesController
5
  from collections import defaultdict
 
17
  self.context = {
18
  "sesion": self.session,
19
  "use_technique": technique,
 
20
  "tipo_escala": technique.escala_tecnica.id_tipo_escala.nombre_escala,
 
21
  "repeticiones_max": technique.repeticiones_max - 2
22
  }
23
 
tecnicas/controllers/views_controller/session_management/{details_rata_controller.py → details/details_rata_controller.py} RENAMED
@@ -1,10 +1,5 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import render, redirect
3
  from tecnicas.models import SesionSensorial
4
- from tecnicas.controllers import PalabrasController, DatoController, CalificacionController
5
- from tecnicas.utils import defaultdict_to_dict
6
  from .details_controller import DetallesController
7
- from collections import defaultdict
8
 
9
 
10
  class DetallesRATAController(DetallesController):
 
 
 
1
  from tecnicas.models import SesionSensorial
 
 
2
  from .details_controller import DetallesController
 
3
 
4
 
5
  class DetallesRATAController(DetallesController):
tecnicas/controllers/views_controller/session_management/details/details_sort_controller.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .details_controller import DetallesController
2
+ from tecnicas.models import SesionSensorial, GrupoProducto, Producto, Participacion
3
+ from collections import defaultdict
4
+
5
+
6
+ class DetallesSortController(DetallesController):
7
+ def __init__(self, session: SesionSensorial):
8
+ super().__init__(session)
9
+ self.url_template = "tecnicas/manage_sesions/details-session-sort.html"
10
+ self.url_next = "cata_system:monitor_sesion"
11
+
12
+ def getContext(self):
13
+ technique = self.session.tecnica
14
+
15
+ finished = False
16
+ status = ""
17
+
18
+ if technique.repeticion < technique.repeticiones_max and not self.session.activo:
19
+ status = "En espera para iniciar la sesión"
20
+ elif technique.repeticion >= technique.repeticiones_max and not self.session.activo:
21
+ status = "Esta sesión ha sido finalizada"
22
+ finished = True
23
+ else:
24
+ status = "La sesión está en progreso"
25
+
26
+ self.context = {
27
+ "sesion": self.session,
28
+ "technique": technique,
29
+ "status": status,
30
+ "finished": finished
31
+ }
32
+
33
+ self.context["data_groups"] = {
34
+ "data": self.setDataSort(),
35
+ "testers": self.setHeaders()
36
+ }
37
+
38
+ return self.context
39
+
40
+ def setDataSort(self):
41
+ data = []
42
+ technique = self.session.tecnica
43
+
44
+ products = Producto.objects.filter(id_tecnica=technique)
45
+
46
+ groups = GrupoProducto.objects.select_related("catador").filter(
47
+ tecnica=technique
48
+ )
49
+
50
+ if len(groups):
51
+ self.context["there_data"] = True
52
+ else:
53
+ self.context["there_data"] = False
54
+ return []
55
+
56
+ for product in products:
57
+ product_data = {
58
+ "codigo_producto": product.codigoProducto,
59
+ "palabras": {}
60
+ }
61
+
62
+ related_groups = groups.filter(productos=product).select_related(
63
+ "catador__user"
64
+ ).prefetch_related("palabras")
65
+
66
+ data_words = defaultdict(set)
67
+
68
+ for group in related_groups:
69
+ catador_username = group.catador.user.username
70
+
71
+ for word in group.palabras.all():
72
+ data_words[catador_username].add(word.nombre_palabra)
73
+
74
+ product_data["palabras"] = {
75
+ username: list(words)
76
+ for username, words in data_words.items()
77
+ }
78
+
79
+ data.append(product_data)
80
+
81
+ return data
82
+
83
+ def setHeaders(self):
84
+ participacions = list(Participacion.objects.filter(
85
+ tecnica=self.session.tecnica
86
+ ).only("catador").select_related("catador__user"))
87
+
88
+ testers = [
89
+ participacion.catador.user.username for participacion in participacions]
90
+
91
+ return testers
tecnicas/controllers/views_controller/session_management/{monitor_controller.py → monitor/monitor_controller.py} RENAMED
@@ -1,9 +1,7 @@
1
  from django.http import HttpRequest
2
  from django.shortcuts import render, redirect
3
  from django.urls import reverse
4
- from tecnicas.models import SesionSensorial, Producto, EsAtributo, EsVocabulario
5
- from tecnicas.controllers import ParticipacionController, SesionController
6
- from tecnicas.utils import controller_error
7
 
8
 
9
  class MonitorController():
@@ -14,20 +12,20 @@ class MonitorController():
14
  self.sensorial_session = session
15
 
16
  def controllPostFinishSession(self, request: HttpRequest):
17
- self.setContext()
18
  (is_all_end, message) = self.checkAllFinish()
19
  if not is_all_end:
 
20
  self.context["error"] = message
21
  return render(request, self.url_view, self.context)
22
  self.finishSession()
23
  return redirect(reverse(self.previus_view, kwargs={"session_code": self.sensorial_session.codigo_sesion}))
24
 
25
- def checkAllFinish(self):
26
  return (False, "Función sin implementar")
27
 
28
  def setContext(self):
29
- self.participations = ParticipacionController.getParticipationsInTechinique(
30
- self.sensorial_session.tecnica)
31
 
32
  self.context = {
33
  "code_session": self.sensorial_session.codigo_sesion,
 
1
  from django.http import HttpRequest
2
  from django.shortcuts import render, redirect
3
  from django.urls import reverse
4
+ from tecnicas.models import SesionSensorial, Producto, EsAtributo, EsVocabulario, Participacion
 
 
5
 
6
 
7
  class MonitorController():
 
12
  self.sensorial_session = session
13
 
14
  def controllPostFinishSession(self, request: HttpRequest):
 
15
  (is_all_end, message) = self.checkAllFinish()
16
  if not is_all_end:
17
+ self.setContext()
18
  self.context["error"] = message
19
  return render(request, self.url_view, self.context)
20
  self.finishSession()
21
  return redirect(reverse(self.previus_view, kwargs={"session_code": self.sensorial_session.codigo_sesion}))
22
 
23
+ def checkAllFinish(self) -> (bool, str):
24
  return (False, "Función sin implementar")
25
 
26
  def setContext(self):
27
+ self.participations = Participacion.objects.filter(
28
+ tecnica=self.sensorial_session.tecnica)
29
 
30
  self.context = {
31
  "code_session": self.sensorial_session.codigo_sesion,
tecnicas/controllers/views_controller/session_management/{monitor_escalas_controller.py → monitor/monitor_escalas_controller.py} RENAMED
File without changes
tecnicas/controllers/views_controller/session_management/{monitor_pf_controller.py → monitor/monitor_pf_controller.py} RENAMED
@@ -1,6 +1,3 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import render, redirect
3
- from django.urls import reverse
4
  from tecnicas.models import Dato, Participacion, Catador, ListaPalabras, Producto
5
  from tecnicas.controllers import SesionController
6
  from .monitor_controller import MonitorController
 
 
 
 
1
  from tecnicas.models import Dato, Participacion, Catador, ListaPalabras, Producto
2
  from tecnicas.controllers import SesionController
3
  from .monitor_controller import MonitorController
tecnicas/controllers/views_controller/session_management/{monitor_rata_controller.py → monitor/monitor_rata_controller.py} RENAMED
File without changes
tecnicas/controllers/views_controller/session_management/monitor/monitor_sort_controller.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import SesionSensorial
2
+ from tecnicas.models import Participacion
3
+ from .monitor_controller import MonitorController
4
+
5
+
6
+ class MonitorSortController(MonitorController):
7
+ def __init__(self, session: SesionSensorial):
8
+ super().__init__(session)
9
+ self.url_view = "tecnicas/manage_sesions/monitor-session-sort.html"
10
+ self.previus_view = "cata_system:detalles_sesion"
11
+
12
+ def checkAllFinish(self):
13
+ technique = self.sensorial_session.tecnica
14
+
15
+ num_participations = Participacion.objects.filter(
16
+ tecnica=technique).count()
17
+
18
+ if num_participations < technique.limite_catadores:
19
+ return (False, "No se ha alcanzado el número máximo de catadores")
20
+
21
+ unfinished_participations = Participacion.objects.filter(
22
+ tecnica=technique, finalizado=False).count()
23
+
24
+ if unfinished_participations > 0:
25
+ return (False, "No todos los catadores han finalizado su evaluación")
26
+
27
+ return (True, "Puedes finalizar la sesión")
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_controller.py CHANGED
@@ -11,7 +11,7 @@ class InitSessionController():
11
  session: SesionSensorial
12
  order: Orden | dict
13
  current_direction: str
14
- current_direction = "tecnicas/forms_tester/init_session.html"
15
  escalas_direction = "cata_system:session_convencional"
16
 
17
  def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
 
11
  session: SesionSensorial
12
  order: Orden | dict
13
  current_direction: str
14
+ current_direction = "tecnicas/forms_tester/init_scales_test.html"
15
  escalas_direction = "cata_system:session_convencional"
16
 
17
  def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_escalas_controller.py CHANGED
@@ -13,7 +13,7 @@ class InitSessionEscalasController(InitSessionController):
13
 
14
  def __init__(self, sensorial_session, user_tester):
15
  super().__init__(sensorial_session, user_tester)
16
- self.current_direction = "tecnicas/forms_tester/init_session.html"
17
  self.escalas_direction = "cata_system:session_convencional"
18
  self.cata_direction = "cata_system:session_cata"
19
 
 
13
 
14
  def __init__(self, sensorial_session, user_tester):
15
  super().__init__(sensorial_session, user_tester)
16
+ self.current_direction = "tecnicas/forms_tester/init_scales_test.html"
17
  self.escalas_direction = "cata_system:session_convencional"
18
  self.cata_direction = "cata_system:session_cata"
19
 
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_pf_controller.py CHANGED
@@ -9,7 +9,7 @@ from .init_session_controller import InitSessionController
9
  class InitSessionPFController(InitSessionController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
- self.current_direction = "tecnicas/forms_tester/init_session_pf.html"
13
  self.pf_direction = "cata_system:session_pf"
14
 
15
  def controllGet(self, request: HttpRequest):
 
9
  class InitSessionPFController(InitSessionController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
+ self.current_direction = "tecnicas/forms_tester/init_pf_test.html"
13
  self.pf_direction = "cata_system:session_pf"
14
 
15
  def controllGet(self, request: HttpRequest):
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_rata_controller.py CHANGED
@@ -6,7 +6,7 @@ from .init_session_controller import InitSessionController
6
  class InitSessionRATAController(InitSessionController):
7
  def __init__(self, sensorial_session, user_tester):
8
  super().__init__(sensorial_session, user_tester)
9
- self.current_direction = "tecnicas/forms_tester/init_session.html"
10
 
11
  def controllGet(self, request: HttpRequest):
12
  context = {
 
6
  class InitSessionRATAController(InitSessionController):
7
  def __init__(self, sensorial_session, user_tester):
8
  super().__init__(sensorial_session, user_tester)
9
+ self.current_direction = "tecnicas/forms_tester/init_scales_test.html"
10
 
11
  def controllGet(self, request: HttpRequest):
12
  context = {
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_sort_controller.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import Participacion
5
+ from tecnicas.controllers import ParticipacionController
6
+ from .init_session_controller import InitSessionController
7
+
8
+
9
+ class InitSessionSortController(InitSessionController):
10
+ def __init__(self, sensorial_session, user_tester):
11
+ super().__init__(sensorial_session, user_tester)
12
+ self.current_direction = "tecnicas/forms_tester/init_test_sort.html"
13
+ self.sort_direction = "cata_system:session_sort"
14
+
15
+ def controllGet(self, request: HttpRequest, error=""):
16
+ context = {
17
+ "session": self.session,
18
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
19
+ }
20
+
21
+ is_end = self.isEndedSession()
22
+
23
+ context["has_ended"] = is_end
24
+
25
+ if is_end:
26
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
27
+
28
+ if "error" in request.GET:
29
+ context["error"] = request.GET["error"]
30
+
31
+ return render(request, self.current_direction, context)
32
+
33
+ def isEndedSession(self):
34
+ participation = Participacion.objects.get(
35
+ catador=self.tester, tecnica=self.session.tecnica)
36
+
37
+ return participation.finalizado
38
+
39
+ def controllPost(self, request: HttpRequest):
40
+ context = {
41
+ "session": self.session,
42
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
43
+ }
44
+
45
+ use_action = request.POST["action"]
46
+
47
+ if use_action == "start_posting":
48
+ parameters = {
49
+ "code_sesion": self.session.codigo_sesion
50
+ }
51
+
52
+ is_end = self.isEndedSession()
53
+ if is_end:
54
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
55
+ return render(request, self.current_direction, context)
56
+
57
+ update_participation = ParticipacionController.enterSession(
58
+ tester=request.user.user_catador, session=self.session)
59
+
60
+ if isinstance(update_participation, dict):
61
+ context["error"] = update_participation["error"]
62
+ return render(request, self.current_direction, context)
63
+
64
+ request.session["id_participation"] = update_participation.id
65
+
66
+ return redirect(reverse(self.sort_direction, kwargs=parameters))
67
+
68
+ elif use_action == "exit_session":
69
+ response = ParticipacionController.outSession(
70
+ tester=request.user.user_catador, session=self.session)
71
+ if isinstance(response, dict):
72
+ context["error"] = response["error"]
73
+ return render(request, self.current_direction, context)
74
+
75
+ else:
76
+ context["error"] = "Acción sin especificar"
77
+ return render(request, self.current_direction, context)
tecnicas/controllers/views_controller/sessions_tester/login_session_tester_controller.py CHANGED
@@ -98,5 +98,51 @@ class LoginSessionTesterController():
98
  context["error"] = "Imposible acceder a esta sesión"
99
  return render(request, self.current_direcction, context)
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  def validateEntryPF(self, request=HttpRequest):
102
  return self.validateEntryEscalas(request=request)
 
98
  context["error"] = "Imposible acceder a esta sesión"
99
  return render(request, self.current_direcction, context)
100
 
101
+ def validateEntrySort(self, request: HttpRequest):
102
+ context = {}
103
+ if not self.session.activo:
104
+ context["error"] = "La sesión no está activa actualmente"
105
+ return render(request, self.current_direcction, context)
106
+
107
+ if self.session.tecnica.repeticion == 1:
108
+ try:
109
+ self.taster_participation = Participacion.objects.get(
110
+ tecnica=self.session.tecnica, catador=self.tester)
111
+ context["error"] = "Usted ya esta dentro de la sesión"
112
+ return render(request, self.current_direcction, context)
113
+
114
+ except Participacion.DoesNotExist:
115
+ try:
116
+ with transaction.atomic():
117
+ code_session = self.session.codigo_sesion
118
+ self.session = SesionSensorial.objects.select_for_update().get(
119
+ codigo_sesion=code_session)
120
+
121
+ max_testers = self.session.tecnica.limite_catadores
122
+ current_num_testers = Participacion.objects.filter(
123
+ tecnica=self.session.tecnica).count()
124
+
125
+ if current_num_testers >= max_testers:
126
+ raise ValueError(
127
+ "La sesión ha alcanzado el número máximo de catadores")
128
+
129
+ self.taster_participation = Participacion.objects.create(
130
+ tecnica=self.session.tecnica,
131
+ catador=self.tester,
132
+ finalizado=False
133
+ )
134
+ params = {
135
+ "code_sesion": self.session.codigo_sesion
136
+ }
137
+ return redirect(reverse(self.destinity_direcction, kwargs=params))
138
+
139
+ except ValueError as e:
140
+ context["error"] = str(e)
141
+ return render(request, self.current_direcction, context)
142
+
143
+ else:
144
+ context["error"] = "Ya no es posible ingresar a la sesión"
145
+ return render(request, self.current_direcction, context)
146
+
147
  def validateEntryPF(self, request=HttpRequest):
148
  return self.validateEntryEscalas(request=request)
tecnicas/controllers/views_controller/sessions_tester/tests_forms/general_test_controller.py CHANGED
@@ -1,7 +1,8 @@
1
  from django.http import HttpRequest
2
  from django.shortcuts import redirect, render
3
  from django.urls import reverse
4
- from tecnicas.models import SesionSensorial, Catador
 
5
 
6
 
7
  class GenetalTestController():
@@ -12,3 +13,16 @@ class GenetalTestController():
12
  def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
13
  self.tester = user_tester
14
  self.session = sensorial_session
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.http import HttpRequest
2
  from django.shortcuts import redirect, render
3
  from django.urls import reverse
4
+ from tecnicas.models import SesionSensorial, Catador, Participacion
5
+ from tecnicas.controllers import ParticipacionController
6
 
7
 
8
  class GenetalTestController():
 
13
  def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
14
  self.tester = user_tester
15
  self.session = sensorial_session
16
+
17
+ def controllPost(self, request: HttpRequest):
18
+ action = request.POST["action"]
19
+
20
+ if action == "finish_session":
21
+ self.participation = Participacion.objects.get(
22
+ tecnica=self.session.tecnica, catador=request.user.user_catador)
23
+ ParticipacionController.finishSession(self.participation)
24
+ params = {"code_sesion": self.session.codigo_sesion}
25
+ return redirect(reverse(self.previus_directory, kwargs=params))
26
+
27
+ else:
28
+ return self.controllGet(request, error="Acción no permitida")
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_cata_controller.py CHANGED
@@ -9,7 +9,7 @@ from .general_test_controller import GenetalTestController
9
  class TestCataController(GenetalTestController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
- self.current_directory = "tecnicas/forms_tester/cata.html"
13
 
14
  def controllGet(self, request: HttpRequest):
15
  technique = self.session.tecnica
 
9
  class TestCataController(GenetalTestController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
+ self.current_directory = "tecnicas/forms_tester/test_cata.html"
13
 
14
  def controllGet(self, request: HttpRequest):
15
  technique = self.session.tecnica
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_rata_controller.py CHANGED
@@ -9,7 +9,7 @@ from .general_test_controller import GenetalTestController
9
  class TestRataController(GenetalTestController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
- self.current_directory = "tecnicas/forms_tester/convencional.html"
13
 
14
  def controllGet(self, request: HttpRequest):
15
  technique = self.session.tecnica
 
9
  class TestRataController(GenetalTestController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
+ self.current_directory = "tecnicas/forms_tester/test_convencional.html"
13
 
14
  def controllGet(self, request: HttpRequest):
15
  technique = self.session.tecnica
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_scales_controller.py CHANGED
@@ -9,7 +9,7 @@ from .general_test_controller import GenetalTestController
9
  class TestScalesController(GenetalTestController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
- self.current_directory = "tecnicas/forms_tester/convencional.html"
13
 
14
  def controllGet(self, request: HttpRequest):
15
  technique = self.session.tecnica
 
9
  class TestScalesController(GenetalTestController):
10
  def __init__(self, sensorial_session, user_tester):
11
  super().__init__(sensorial_session, user_tester)
12
+ self.current_directory = "tecnicas/forms_tester/test_convencional.html"
13
 
14
  def controllGet(self, request: HttpRequest):
15
  technique = self.session.tecnica
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_sort_controller.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.models import Participacion, Producto, Calificacion, Palabra, GrupoProducto
5
+ from tecnicas.controllers import ParticipacionController, PalabrasController, EscalaController
6
+ from tecnicas.forms import ListWordsForm
7
+ from .general_test_controller import GenetalTestController
8
+
9
+
10
+ class TestSortController(GenetalTestController):
11
+ def __init__(self, sensorial_session, user_tester):
12
+ super().__init__(sensorial_session, user_tester)
13
+ self.current_directory = "tecnicas/forms_tester/test_sort.html"
14
+
15
+ def controllGet(self, request: HttpRequest):
16
+ '''
17
+ Objetivo: Entregar al cliente los grupos de productos guardados hechos por el catador en una lista, de lo contrario solo mandar una lista vacia
18
+ - Comprobar que el Catador aun no finalice su la sesion
19
+ - Obtener todos los productos en la tecnica
20
+ - Obtener todos los grupos creadas por el usuario
21
+ - Si hay grupos, cada item de la lista a mandar debe incluir los productos asociados al grupo como las palabras que describen al grupo
22
+ - Si no hay grupos, solo mandar una lista vacia
23
+ - Mandar la lista de grupos como la lista de productos
24
+ - Agregar el formulario para describir los grupo
25
+ '''
26
+ self.context["session"] = self.session
27
+ technique = self.session.tecnica
28
+
29
+ self.participation = Participacion.objects.get(
30
+ tecnica=technique, catador=request.user.user_catador)
31
+
32
+ # Comprobar que el Catador no haya finalizado
33
+ if self.participation.finalizado:
34
+ params = {
35
+ "code_sesion": self.session.codigo_sesion
36
+ }
37
+ return redirect(reverse(self.previus_directory, kwargs=params))
38
+
39
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
40
+ self.context["products"] = products_in_technique
41
+
42
+ grups_products = GrupoProducto.objects.filter(
43
+ tecnica=technique, catador=request.user.user_catador).select_related("productos", "palabras")
44
+
45
+ self.context["grups_products"] = grups_products if grups_products else []
46
+
47
+ self.context["form_word"] = ListWordsForm()
48
+
49
+ return render(request, self.current_directory, self.context)
50
+
tecnicas/forms/__init__.py CHANGED
@@ -2,6 +2,8 @@ from .create_session.sesion_basic_form import SesionBasicForm
2
  from .create_session.sesiob_basic_cata_form import SesionBasicCATAForm
3
  from .create_session.sesion_basic_pf_form import SesionBasicPFForm
4
  from .create_session.sesion_tags_form import SesionTagsForm
 
 
5
 
6
  from .etiqueta_form import EtiquetaForm
7
  from .codes_form import CodesForm
 
2
  from .create_session.sesiob_basic_cata_form import SesionBasicCATAForm
3
  from .create_session.sesion_basic_pf_form import SesionBasicPFForm
4
  from .create_session.sesion_tags_form import SesionTagsForm
5
+ from .create_session.sesion_basic_sort_form import SesionBasicSortForm
6
+ from .create_session.sesion_basic_napping import SesionBasicNappingForm
7
 
8
  from .etiqueta_form import EtiquetaForm
9
  from .codes_form import CodesForm
tecnicas/forms/create_session/sesiob_basic_cata_form.py CHANGED
@@ -22,6 +22,11 @@ class SesionBasicCATAForm(forms.Form):
22
  def __init__(self, *args, **kwargs):
23
  super().__init__(*args, **kwargs)
24
 
25
- self.fields['estilo_palabras'] = forms.ModelChoiceField(queryset=EstiloPalabra.objects.all(), widget=forms.RadioSelect(attrs={
 
 
 
 
 
26
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
27
- }), required=True, initial=EstiloPalabra.objects.first())
 
22
  def __init__(self, *args, **kwargs):
23
  super().__init__(*args, **kwargs)
24
 
25
+ options = [
26
+ ("atributos", "atributos"),
27
+ ("vocabulario", "vocabulario")
28
+ ]
29
+
30
+ self.fields['estilo_palabras'] = forms.ChoiceField(choices=options, widget=forms.RadioSelect(attrs={
31
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
32
+ }), required=True, initial=options[0])
tecnicas/forms/create_session/sesion_basic_form.py CHANGED
@@ -38,9 +38,14 @@ class SesionBasicForm(forms.Form):
38
  if initial_conf is None:
39
  initial_conf = {}
40
 
41
- self.fields['estilo_palabras'] = forms.ModelChoiceField(queryset=EstiloPalabra.objects.all(), widget=forms.RadioSelect(attrs={
 
 
 
 
 
42
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
43
- }), required=True, initial=EstiloPalabra.objects.first())
44
 
45
  self.fields['tipo_escala'] = forms.ModelChoiceField(queryset=TipoEscala.objects.all(), widget=forms.RadioSelect(attrs={
46
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
 
38
  if initial_conf is None:
39
  initial_conf = {}
40
 
41
+ options = [
42
+ ("atributos", "atributos"),
43
+ ("vocabulario", "vocabulario")
44
+ ]
45
+
46
+ self.fields['estilo_palabras'] = forms.ChoiceField(choices=options, widget=forms.RadioSelect(attrs={
47
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
48
+ }), required=True, initial=options[0])
49
 
50
  self.fields['tipo_escala'] = forms.ModelChoiceField(queryset=TipoEscala.objects.all(), widget=forms.RadioSelect(attrs={
51
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
tecnicas/forms/create_session/sesion_basic_napping.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django import forms
2
+
3
+
4
+ class SesionBasicNappingForm(forms.Form):
5
+ nombre_sesion = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
6
+ "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
7
+ "name": "nombre_sesion",
8
+ "placeholder": "Ej. Mermelada de mango picante"
9
+ }), required=False)
10
+
11
+ numero_productos = forms.IntegerField(widget=forms.NumberInput(attrs={
12
+ "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
13
+ "placeholder": "Solo números"
14
+ }), required=True)
15
+
16
+ numero_catadores = forms.IntegerField(widget=forms.NumberInput(attrs={
17
+ "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
18
+ "placeholder": "Solo números"
19
+ }), required=True)
20
+
21
+ instrucciones = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
22
+ "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
23
+ "placeholder": "Este campo es opcional"
24
+ }), required=False)
tecnicas/forms/create_session/sesion_basic_pf_form.py CHANGED
@@ -1,5 +1,4 @@
1
  from django import forms
2
- from tecnicas.models import TipoEscala
3
 
4
 
5
  class SesionBasicPFForm(forms.Form):
 
1
  from django import forms
 
2
 
3
 
4
  class SesionBasicPFForm(forms.Form):
tecnicas/forms/create_session/sesion_basic_sort_form.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django import forms
2
+
3
+
4
+ class SesionBasicSortForm(forms.Form):
5
+ nombre_sesion = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
6
+ "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
7
+ "name": "nombre_sesion",
8
+ "placeholder": "Ej. Mermelada de mango picante"
9
+ }), required=False)
10
+
11
+ numero_productos = forms.IntegerField(widget=forms.NumberInput(attrs={
12
+ "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
13
+ "placeholder": "Solo números"
14
+ }), required=True)
15
+
16
+ numero_catadores = forms.IntegerField(widget=forms.NumberInput(attrs={
17
+ "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
18
+ "placeholder": "Solo números"
19
+ }), required=True)
20
+
21
+ instrucciones = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
22
+ "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
23
+ "placeholder": "Este campo es opcional"
24
+ }), required=False)
tecnicas/migrations/0024_alter_sesionsensorial_codigo_sesion_grupoproducto.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-11-25 01:18
2
+
3
+ import django.db.models.deletion
4
+ import shortuuid.main
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('tecnicas', '0023_alter_sesionsensorial_codigo_sesion_listapalabras'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name='sesionsensorial',
17
+ name='codigo_sesion',
18
+ field=models.CharField(default=shortuuid.main.ShortUUID.uuid, editable=False, max_length=22, primary_key=True, serialize=False),
19
+ ),
20
+ migrations.CreateModel(
21
+ name='GrupoProducto',
22
+ fields=[
23
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24
+ ('catador', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='catador_grupo_producto', to='tecnicas.catador')),
25
+ ('palabras', models.ManyToManyField(related_name='grupo_producto', to='tecnicas.palabra')),
26
+ ('productos', models.ManyToManyField(related_name='grupo_producto', to='tecnicas.producto')),
27
+ ('tecnica', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tecnica_grupo_producto', to='tecnicas.tecnica')),
28
+ ],
29
+ ),
30
+ ]
tecnicas/models/__init__.py CHANGED
@@ -30,3 +30,4 @@ from .orden import Posicion
30
  from .participacion import Participacion
31
 
32
  from .lista_palabras import ListaPalabras
 
 
30
  from .participacion import Participacion
31
 
32
  from .lista_palabras import ListaPalabras
33
+ from .grupo_producto import GrupoProducto
tecnicas/models/grupo_producto.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.db import models
2
+ from tecnicas.models import Tecnica, Catador, Palabra, Producto
3
+
4
+
5
+ class GrupoProducto(models.Model):
6
+ tecnica = models.ForeignKey(
7
+ Tecnica, on_delete=models.CASCADE, related_name="tecnica_grupo_producto")
8
+
9
+ catador = models.ForeignKey(
10
+ Catador, on_delete=models.CASCADE, related_name="catador_grupo_producto")
11
+
12
+ productos = models.ManyToManyField(
13
+ Producto, related_name="grupo_producto")
14
+
15
+ palabras = models.ManyToManyField(
16
+ Palabra, related_name="grupo_producto")
17
+
18
+ def __str__(self):
19
+ return f"{self.tecnica.sesion_tecnica.codigo_sesion} - {self.catador.user.username}"
tecnicas/static/js/download-table-csv.js CHANGED
@@ -4,7 +4,7 @@ document.addEventListener("DOMContentLoaded", function () {
4
 
5
  btn.addEventListener("click", function () {
6
  // Try set the table in the page
7
- let table = document.getElementById("convencional-table");
8
  if (!table) {
9
  const section = btn.closest("section");
10
  if (section) table = section.querySelector("table");
 
4
 
5
  btn.addEventListener("click", function () {
6
  // Try set the table in the page
7
+ let table = document.getElementById("generic-donwload-table");
8
  if (!table) {
9
  const section = btn.closest("section");
10
  if (section) table = section.querySelector("table");
tecnicas/static/js/download-table-sort-csv.js ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ const btn = document.getElementById("download-csv-btn");
3
+ if (!btn) return;
4
+
5
+ btn.addEventListener("click", function () {
6
+ // Try set the table in the page
7
+ let table = document.getElementById("generic-donwload-table");
8
+ if (!table) {
9
+ const section = btn.closest("section");
10
+ if (section) table = section.querySelector("table");
11
+ }
12
+ if (!table) {
13
+ console.warn("No se encontró la tabla para descargar.");
14
+ return;
15
+ }
16
+
17
+ // helper to trim and normalize cell text
18
+ const cellText = (cell) => {
19
+ if (!cell) return "";
20
+ return String(cell.textContent || "").trim();
21
+ };
22
+
23
+ // Collect headers
24
+ const headers = [];
25
+ const ths = table.querySelectorAll("thead th");
26
+ ths.forEach((th) => headers.push(cellText(th)));
27
+
28
+ // Collect rows
29
+ const rows = [];
30
+ const trs = table.querySelectorAll("tbody tr");
31
+ trs.forEach((tr) => {
32
+ const cols = [];
33
+ const tds = tr.querySelectorAll("td");
34
+ tds.forEach((td) => cols.push(cellText(td)));
35
+ rows.push(cols);
36
+ });
37
+
38
+ // Convert to CSV string (escape quotes, wrap in quotes if needed)
39
+ const escapeValue = (val) => {
40
+ if (val == null) return "";
41
+ let normalVal = val.replace(/[\u0300-\u036f]/g, "");
42
+ normalVal = normalVal.replace(/(\r\n|\n|\r|\s)/gm, '');
43
+ const needsQuotes = /[",\n,;]/.test(normalVal);
44
+ let v = normalVal.replace(/"/g, '""');
45
+ if (needsQuotes) v = `"${v}"`;
46
+ return v;
47
+ };
48
+
49
+ const lines = [];
50
+ if (headers.length) lines.push(headers.map(escapeValue).join(","));
51
+ rows.forEach((r) => lines.push(r.map(escapeValue).join(",")));
52
+
53
+ const csvContent = lines.join("\n");
54
+
55
+ // File name: data_{nombre_sesion or codigo_sesion}
56
+ const rawName = (btn.dataset.sessionName || "").trim();
57
+ const code = (btn.dataset.sessionCode || "").trim() || "session";
58
+ const namePart = rawName
59
+ ? rawName.replace(/[^a-zA-Z0-9-_áéíóúÁÉÍÓÚ ]/g, "").replace(/\s+/g, "_")
60
+ : code;
61
+ const fileName = `data_${namePart}.csv`;
62
+
63
+ // Create blob and force download
64
+ const blob = new Blob([csvContent], { type: "text/csv;charset=UTF-8;" });
65
+ if (navigator.msSaveBlob) {
66
+ navigator.msSaveBlob(blob, fileName);
67
+ } else {
68
+ const link = document.createElement("a");
69
+ const url = URL.createObjectURL(blob);
70
+ link.setAttribute("href", url);
71
+ link.setAttribute("download", fileName);
72
+ link.style.visibility = "hidden";
73
+ document.body.appendChild(link);
74
+ link.click();
75
+ document.body.removeChild(link);
76
+ URL.revokeObjectURL(url);
77
+ }
78
+ });
79
+ });
tecnicas/static/js/test-sort.js ADDED
@@ -0,0 +1,554 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let dragged = null;
2
+
3
+ const productsPlaceHolder = `
4
+ <p class="text-products text-center text-lg font-medium">
5
+ Agrupación de productos
6
+ </p>`;
7
+
8
+ const wordsPlaceHolder = `
9
+ <p class="text-words text-center text-lg font-medium">
10
+ Lista de atributos
11
+ </p>`;
12
+
13
+ const products = document.querySelectorAll(".draggable");
14
+ const zonesDrop = document.querySelectorAll(".dropzone");
15
+
16
+ const DATA_GRUPS = {};
17
+
18
+ products.forEach(manageDragables);
19
+ zonesDrop.forEach(manageDropZone);
20
+
21
+
22
+ /*
23
+ ////
24
+ //////
25
+ //////// Add new grups
26
+ //////
27
+ ////
28
+ */
29
+
30
+ function addNewGrup() {
31
+ const container = document.getElementById("containers");
32
+ const newGrup = document.createElement("section");
33
+ const styles = "w-fit space-y-4 border rounded p-2 bg-surface-sweet";
34
+ newGrup.classList.add(...styles.split(" "));
35
+
36
+ const formWord = document.getElementById("form-word");
37
+ const formWordClone = formWord.cloneNode(true);
38
+ formWordClone.classList.remove("hidden");
39
+
40
+ newGrup.innerHTML = `
41
+ <div class="dropzone w-64 min-h-16 bg-surface-alter-card rounded p-2 flex items-center justify-center flex-wrap gap-2">
42
+ ${productsPlaceHolder}
43
+ </div>
44
+ ${formWordClone.outerHTML}
45
+ <div
46
+ class="words-container w-64 min-h-16 bg-surface-alter rounded p-2 flex items-center justify-center flex-wrap gap-2">
47
+ ${wordsPlaceHolder}
48
+ </div>
49
+ <div class="cts-container-question flex items-center justify-around gap-2">
50
+ <button class="cts-question cts-btn-general-compress flex-1 cts-btn-secondary btn-push">
51
+ ¿Remover este grupo?
52
+ </button>
53
+ <button class="cts-remove cts-btn-general-compress flex-1 cts-btn-primary btn-push hidden">
54
+ Remover
55
+ </button>
56
+ <button class="cts-no-remove cts-btn-general-compress flex-1 cts-btn-error btn-push hidden">
57
+ Conservar
58
+ </button>
59
+ </div>`;
60
+
61
+ manageDropZone(newGrup.querySelector(".dropzone"));
62
+ container.appendChild(newGrup);
63
+ }
64
+
65
+
66
+ /*
67
+ ////
68
+ //////
69
+ //////// Manage products' drags and remove drags
70
+ //////
71
+ ////
72
+ */
73
+
74
+ /**
75
+ *
76
+ * @param {HTMLElement} zone
77
+ */
78
+ function manageDropZone(zone) {
79
+ /*
80
+ ////
81
+ ////// Trash Zone product
82
+ ////
83
+ */
84
+ if (zone.classList.contains("trash-products")) {
85
+ zone.addEventListener("dragover", (e) => e.preventDefault());
86
+
87
+ zone.addEventListener("drop", (event) => {
88
+ const codeDrag = dragged.dataset.code
89
+ const oldParent = dragged.parentNode;
90
+ const oldCodeZone = oldParent.getAttribute("id")
91
+
92
+ if (oldParent.classList.contains("original-products")) {
93
+ spanNotifaction("No puedes borrar este producto");
94
+ return
95
+ }
96
+
97
+ dragged.remove()
98
+ removeProduct(codeDrag, oldCodeZone)
99
+
100
+ spanNotifaction("Producto removido del grupo", false);
101
+ dragged = null
102
+
103
+ if (oldParent.classList.contains("dropzone") && oldParent.children.length === 0) {
104
+ oldParent.innerHTML = productsPlaceHolder;
105
+ }
106
+ })
107
+ return
108
+ }
109
+
110
+ /*
111
+ ////
112
+ ////// Identificate grups and creata dinamic data
113
+ ////
114
+ */
115
+
116
+ // Create id for grup
117
+ const idZone = generateSimpleID()
118
+
119
+ // Add products if there en the page
120
+ const products = []
121
+ const productsElements = zone.querySelectorAll(".draggable") || None;
122
+
123
+ if (productsElements) {
124
+ productsElements.forEach((proEle) => {
125
+ products.push({
126
+ id: proEle.dataset.idProduct,
127
+ code: proEle.dataset.code
128
+ })
129
+ })
130
+ }
131
+
132
+ const parentZone = zone.parentNode;
133
+ const wordsInGrup = parentZone.querySelectorAll(".item-word") || None
134
+
135
+ // Add words if there en the page
136
+ const words = []
137
+ if (wordsInGrup) {
138
+ wordsInGrup.forEach((wordElement) => {
139
+ words.push(wordElement.textContent)
140
+ })
141
+ }
142
+
143
+ DATA_GRUPS[idZone] = {
144
+ products: products,
145
+ words: words
146
+ }
147
+
148
+ /*
149
+ ////
150
+ ////// Drop Zone product
151
+ ////
152
+ */
153
+
154
+ zone.setAttribute("id", idZone)
155
+ zone.addEventListener("dragover", (e) => e.preventDefault());
156
+
157
+ zone.addEventListener("drop", () => {
158
+ const codeDrag = dragged.dataset.code
159
+ const zoneCode = zone.getAttribute("id")
160
+
161
+ // Check that new produck is not in currents products of the group
162
+ const obj = DATA_GRUPS[zoneCode].products.find((product) => product.code == codeDrag)
163
+ if (obj) {
164
+ spanNotifaction("Ese producto ya está en el grupo");
165
+ return
166
+ }
167
+
168
+ DATA_GRUPS[zoneCode].products.push({
169
+ id: dragged.dataset.idProduct,
170
+ code: dragged.dataset.code
171
+ })
172
+
173
+ const oldParent = dragged.parentNode;
174
+
175
+ const p = zone.querySelector("p");
176
+ if (p) {
177
+ p.remove();
178
+ }
179
+
180
+ // Move original or clone product in zone
181
+ if (oldParent.classList.contains("dropzone")) {
182
+ zone.appendChild(dragged);
183
+ } else {
184
+ const clone = dragged.cloneNode(true);
185
+ clone.classList.remove("opacity-50");
186
+ manageDragables(clone);
187
+ zone.appendChild(clone);
188
+ }
189
+
190
+ if (oldParent.classList.contains("dropzone")) {
191
+ if (oldParent.children.length === 0) oldParent.innerHTML = productsPlaceHolder;
192
+ const zoneCodeOld = oldParent.getAttribute("id")
193
+ removeProduct(codeDrag, zoneCodeOld)
194
+ }
195
+ });
196
+
197
+ // Add listeners to elements options and WordForm for the list words
198
+ addListenersButtonQuestionGrup(zone.parentNode)
199
+ manageFormWord(parentZone.querySelector("form"), parentZone.querySelector(".words-container"), idZone)
200
+
201
+ if (DATA_GRUPS[idZone].words.length) {
202
+ parentZone.querySelector(".words-container").innerHTML = "";
203
+ DATA_GRUPS[idZone].words.forEach((word) => {
204
+ const itemWord = getItemWord(word, idZone);
205
+ parentZone.querySelector(".words-container").appendChild(itemWord);
206
+ })
207
+ }
208
+ }
209
+
210
+ /*
211
+ ////
212
+ //////
213
+ //////// Management Drags and FormWord
214
+ //////
215
+ ////
216
+ */
217
+
218
+ /**
219
+ *
220
+ * @param {HTMLElement} el
221
+ */
222
+ function manageDragables(el) {
223
+ el.addEventListener("dragstart", () => {
224
+ dragged = el;
225
+ setTimeout(() => el.classList.add("opacity-50"), 0);
226
+ });
227
+
228
+ el.addEventListener("dragend", () => {
229
+ dragged = null;
230
+ el.classList.remove("opacity-50");
231
+ });
232
+ }
233
+
234
+ /**
235
+ *
236
+ * @param {HTMLFormElementt} form
237
+ * @param {HTMLElement} containerWords
238
+ */
239
+ function manageFormWord(form, containerWords, codeGrup) {
240
+ form.addEventListener("submit", (e) => {
241
+ e.preventDefault();
242
+
243
+ if (!form.reportValidity()) {
244
+ return;
245
+ }
246
+
247
+ const input = form.querySelector('input[type="text"]');
248
+
249
+ if (!input) return;
250
+
251
+ const name = input.value.trim();
252
+ if (!name) return;
253
+
254
+ if (DATA_GRUPS[codeGrup].words.includes(name)) {
255
+ input.value = "";
256
+ input.focus();
257
+ spanNotifaction("Esa palabra ya está en la lista");
258
+ return;
259
+ }
260
+
261
+ DATA_GRUPS[codeGrup].words.push(name)
262
+ const wordItem = getItemWord(name, codeGrup)
263
+
264
+ const p = containerWords.querySelector("p");
265
+ if (p) {
266
+ p.remove();
267
+ }
268
+
269
+ containerWords.appendChild(wordItem)
270
+
271
+ input.value = "";
272
+ input.focus();
273
+ });
274
+ }
275
+
276
+ /*
277
+ ////
278
+ //////
279
+ //////// Management Drags and FormWord
280
+ //////
281
+ ////
282
+ */
283
+
284
+ /**
285
+ *
286
+ * @param {HTMLElement} grup
287
+ */
288
+ function removeGrup(grup) {
289
+ delete DATA_GRUPS[grup.querySelector(".dropzone").getAttribute("id")]
290
+ grup.remove()
291
+ }
292
+
293
+ function removeWord(code, wordName) {
294
+ const newWords = DATA_GRUPS[code].words.filter(word => word != wordName)
295
+ DATA_GRUPS[code].words = newWords
296
+ if (!newWords.length) {
297
+ const containerWords = document.getElementById(code).parentNode.querySelector(".words-container")
298
+ containerWords.innerHTML = wordsPlaceHolder;
299
+ }
300
+ }
301
+
302
+ function removeProduct(codeProduct, codeZone) {
303
+ const newProducts = DATA_GRUPS[codeZone].products.filter((product) => product.code != codeProduct)
304
+ DATA_GRUPS[codeZone].products = newProducts
305
+ }
306
+
307
+ function getItemWord(wordName, code) {
308
+ const STYLES_DIV = "cts-item-words bg-surface-ligt text-black rounded font-bold text-lg p-1 flex flex-wrap flex-row flex-1 min-w-fit justify-center items-center gap-3";
309
+ const STYLES_BTN = "cts-remove-word cts-btn-general-compress px-2 cts-btn-error"
310
+
311
+ const btn = document.createElement("button");
312
+ btn.setAttribute("data-code", code);
313
+ btn.setAttribute("data-word", wordName);
314
+ btn.classList.add(...STYLES_BTN.split(" "));
315
+ btn.textContent = "➖";
316
+
317
+ const span = document.createElement("span");
318
+ span.classList.add("item-word");
319
+ span.textContent = wordName;
320
+
321
+ const div = document.createElement("div");
322
+ div.setAttribute("id", `word-${code}-${wordName}`);
323
+ div.classList.add(...STYLES_DIV.split(" "));
324
+
325
+ div.appendChild(span);
326
+ div.appendChild(btn);
327
+
328
+ btn.addEventListener("click", (e) => {
329
+ removeWord(code, wordName)
330
+ div.remove()
331
+ })
332
+
333
+ return div;
334
+ };
335
+
336
+ /**
337
+ *
338
+ * @param {HTMLElement} grupContainer
339
+ */
340
+ function addListenersButtonQuestionGrup(grupContainer) {
341
+ // Function hidden question
342
+ grupContainer.querySelector(".cts-question").addEventListener("click", (event) => {
343
+ hiddenQuestionRemoveGroup(grupContainer)
344
+ })
345
+
346
+ // Function cancel remove
347
+ grupContainer.querySelector(".cts-no-remove").addEventListener("click", (event) => {
348
+ hiddenQuestionRemoveGroup(grupContainer, false)
349
+ })
350
+
351
+ // Function remove grup
352
+ grupContainer.querySelector(".cts-remove").addEventListener("click", (event) => {
353
+ removeGrup(grupContainer)
354
+ })
355
+ }
356
+
357
+ /*
358
+ ////
359
+ //////
360
+ //////// Management Options Save and remove groups
361
+ //////
362
+ ////
363
+ */
364
+
365
+ /**
366
+ *
367
+ * @param {HTMLDivElement} container
368
+ * @param {boolean} hiddenQuestion
369
+ */
370
+ function hiddenQuestionRemoveGroup(container, hiddenQuestion = true) {
371
+ const question = container.querySelector(".cts-question")
372
+ const remove = container.querySelector(".cts-remove")
373
+ const noRemove = container.querySelector(".cts-no-remove")
374
+
375
+
376
+ if (hiddenQuestion) {
377
+ question.classList.add("hidden")
378
+ remove.classList.remove("hidden")
379
+ noRemove.classList.remove("hidden")
380
+ } else {
381
+ question.classList.remove("hidden")
382
+ remove.classList.add("hidden")
383
+ noRemove.classList.add("hidden")
384
+ }
385
+ }
386
+
387
+ function generateSimpleID() {
388
+ const first = Date.now().toString(35);
389
+ const second = Math.random().toString(36).slice(2);
390
+ return first + second;
391
+ }
392
+
393
+ function showOptionsSave() {
394
+ document.getElementById("question-save").classList.add("hidden");
395
+ document.getElementById("finish-session").classList.remove("hidden");
396
+ document.getElementById("cancel-save").classList.remove("hidden");
397
+ }
398
+
399
+ function showQuestionSave() {
400
+ document.getElementById("question-save").classList.remove("hidden");
401
+ document.getElementById("finish-session").classList.add("hidden");
402
+ document.getElementById("cancel-save").classList.add("hidden");
403
+ }
404
+
405
+ document
406
+ .getElementById("question-save")
407
+ .addEventListener("click", showOptionsSave);
408
+
409
+ document
410
+ .getElementById("cancel-save")
411
+ .addEventListener("click", showQuestionSave);
412
+
413
+ /*
414
+ ////
415
+ //////
416
+ //////// Save data
417
+ //////
418
+ ////
419
+ */
420
+
421
+ async function saveData() {
422
+ const keysDataGrups = Object.keys(DATA_GRUPS);
423
+ const allCodesProducts = new Set()
424
+
425
+ products.forEach((productElement) => {
426
+ allCodesProducts.add(productElement.getAttribute("data-code"))
427
+ })
428
+
429
+ if (keysDataGrups.length === 0) {
430
+ spanNotifaction("No hay grupos para guardar")
431
+ return false;
432
+ }
433
+
434
+ const data = []
435
+ let thereError = false;
436
+ const codesProducts = []
437
+
438
+ keysDataGrups.forEach((key) => {
439
+ if (thereError) return
440
+
441
+ const dataGrup = DATA_GRUPS[key];
442
+ if (!dataGrup) {
443
+ spanNotifaction("No hay datos para guardar")
444
+ thereError = true;
445
+ return
446
+ }
447
+
448
+ if (dataGrup.products.length === 0) {
449
+ spanNotifaction("Los grupos deben tener por lo menos un producto para guardar")
450
+ thereError = true;
451
+ return
452
+ }
453
+
454
+ if (dataGrup.words.length === 0) {
455
+ spanNotifaction("Los grupos deben tener por lo menos una palabra para guardar")
456
+ thereError = true;
457
+ return
458
+ }
459
+
460
+ const words = dataGrup.words;
461
+ const products = dataGrup.products;
462
+
463
+ codesProducts.push(...products.map((product) => product.code))
464
+
465
+ data.push({
466
+ words,
467
+ products
468
+ })
469
+ })
470
+
471
+ if (thereError) return false;
472
+
473
+ const currentCodesProducts = new Set(codesProducts)
474
+ const difference = symmetricDifference(currentCodesProducts, allCodesProducts)
475
+
476
+ if (difference.size > 0) {
477
+ spanNotifaction("Falta un producto que debe ser ordenado")
478
+ return false
479
+ }
480
+
481
+ const URL = "/cata/testers/api/rating-sort"
482
+ const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
483
+
484
+ try {
485
+ const response = await fetch(URL, {
486
+ method: "POST",
487
+ headers: {
488
+ "Content-Type": "application/json",
489
+ "X-CSRFToken": csrfToken,
490
+ },
491
+ body: JSON.stringify(data),
492
+ })
493
+
494
+ if (!response.ok) {
495
+ spanNotifaction("Error en la respuesta del servidor")
496
+ return false;
497
+ }
498
+
499
+ const result = await response.json()
500
+
501
+ if (result.error) {
502
+ spanNotifaction(result.error)
503
+ return false
504
+ } else {
505
+ spanNotifaction(result.message, false)
506
+ return true
507
+ }
508
+ } catch (error) {
509
+ spanNotifaction("Error en proceso de guardar los datos")
510
+ return false
511
+ }
512
+ }
513
+
514
+ const buttonSaveData = document.getElementById("save-progress")
515
+ buttonSaveData.addEventListener("click", saveData)
516
+
517
+ function symmetricDifference(setA, setB) {
518
+ let _difference = new Set(setA);
519
+ for (let elem of setB) {
520
+ if (_difference.has(elem)) {
521
+ _difference.delete(elem);
522
+ } else {
523
+ _difference.add(elem);
524
+ }
525
+ }
526
+ return _difference;
527
+ }
528
+
529
+ /*
530
+ ////
531
+ //////
532
+ //////// Finish session
533
+ //////
534
+ ////
535
+ */
536
+
537
+ document
538
+ .getElementById("finish-session")
539
+ .addEventListener("click", finishSession);
540
+
541
+ async function finishSession() {
542
+ const save = await saveData();
543
+ if (!save) {
544
+ spanNotifaction("Error al guardar los datos")
545
+ return
546
+ };
547
+
548
+ const FORM_ACTION = document.querySelector(".form-actions")
549
+
550
+ const inputAction = FORM_ACTION.querySelector(".action-input");
551
+ FORM_ACTION.action = "";
552
+ inputAction.value = "finish_session";
553
+ FORM_ACTION.submit();
554
+ }
tecnicas/templates/tecnicas/components/table-scales.html CHANGED
@@ -1,7 +1,7 @@
1
  {% load static %}
2
  <section>
3
  <div class="overflow-x-auto rounded-lg border border-surface-general">
4
- <table id="convencional-table" class="min-w-max w-full text-sm text-center border-collapse">
5
  <thead class="bg-surface-sweet text-black font-semibold">
6
  <tr>
7
  {% if sesion.tecnica.tipo_tecnica.nombre_tecnica != "rata" %}
 
1
  {% load static %}
2
  <section>
3
  <div class="overflow-x-auto rounded-lg border border-surface-general">
4
+ <table id="generic-donwload-table" class="min-w-max w-full text-sm text-center border-collapse">
5
  <thead class="bg-surface-sweet text-black font-semibold">
6
  <tr>
7
  {% if sesion.tecnica.tipo_tecnica.nombre_tecnica != "rata" %}