chartManD commited on
Commit
b79118c
·
2 Parent(s): 4f69c19 4b4e7f3

Merge branch 'dev'

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. tecnicas/constants.py +6 -0
  2. tecnicas/controllers/__init__.py +14 -2
  3. tecnicas/controllers/models_controller/calificacion_controller.py +4 -5
  4. tecnicas/controllers/models_controller/dato_controller.py +1 -1
  5. tecnicas/controllers/models_controller/escala_controller.py +2 -2
  6. tecnicas/controllers/models_controller/ordenes_controller.py +7 -7
  7. tecnicas/controllers/models_controller/palabras_controller.py +3 -5
  8. tecnicas/controllers/models_controller/particiapacion_controller.py +7 -5
  9. tecnicas/controllers/models_controller/sesion_controller.py +15 -11
  10. tecnicas/controllers/models_controller/tecnica_controller.py +6 -6
  11. tecnicas/controllers/views_controller/create_session/panel_basic_controller.py +105 -0
  12. tecnicas/controllers/views_controller/create_session/panel_codes_controller.py +114 -0
  13. tecnicas/controllers/views_controller/create_session/panel_create_controller.py +274 -0
  14. tecnicas/controllers/views_controller/create_session/panel_tags_controller.py +71 -0
  15. tecnicas/controllers/views_controller/create_session/panel_words_controller.py +49 -0
  16. tecnicas/controllers/views_controller/list_sessions_tester_controller.py +76 -0
  17. tecnicas/controllers/views_controller/login_tester_controller.py +55 -19
  18. tecnicas/controllers/views_controller/main_tester_form_controller.py +13 -12
  19. tecnicas/controllers/views_controller/session_management/details_controller.py +13 -0
  20. tecnicas/controllers/views_controller/{detalles_sesion_controller.py → session_management/details_escala_controller.py} +42 -34
  21. tecnicas/controllers/views_controller/session_management/details_rata_controller.py +56 -0
  22. tecnicas/controllers/views_controller/session_management/monitor_controller.py +10 -0
  23. tecnicas/controllers/views_controller/{monitor_sesion_controller.py → session_management/monitor_escalas_controller.py} +45 -34
  24. tecnicas/controllers/views_controller/tester_list_controller.py +58 -0
  25. tecnicas/decorators/required_presenter.py +1 -1
  26. tecnicas/forms/catador_form.py +144 -21
  27. tecnicas/forms/codes_form.py +3 -2
  28. tecnicas/forms/sesion_basic_form.py +18 -23
  29. tecnicas/forms/sesion_tags_form.py +25 -15
  30. tecnicas/middlewares/presenter_middleware.py +2 -3
  31. tecnicas/middlewares/tester_middleware.py +3 -28
  32. tecnicas/models/participacion.py +1 -1
  33. tecnicas/models/sesion_sensorial.py +6 -1
  34. tecnicas/static/js/catador-craete.js +0 -1
  35. tecnicas/static/js/create-session.js +2 -2
  36. tecnicas/static/js/details-session.js +9 -15
  37. tecnicas/static/js/panel-basic.js +76 -4
  38. tecnicas/static/js/panel-words.js +1 -1
  39. tecnicas/static/js/showHiddenElement.js +9 -0
  40. tecnicas/templates/tecnicas/auth.html +1 -1
  41. tecnicas/templates/tecnicas/cata-login.html +6 -25
  42. tecnicas/templates/tecnicas/components/item-tester.html +39 -0
  43. tecnicas/templates/tecnicas/components/item_session.html +47 -0
  44. tecnicas/templates/tecnicas/components/item_session_tester.html +40 -0
  45. tecnicas/templates/tecnicas/components/table-convencional.html +0 -1
  46. tecnicas/templates/tecnicas/create_sesion/configuracion-panel-basic.html +52 -10
  47. tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html +9 -0
  48. tecnicas/templates/tecnicas/create_sesion/configuracion-panel-tags.html +11 -23
  49. tecnicas/templates/tecnicas/create_sesion/seleccion-tecnica.html +1 -1
  50. tecnicas/templates/tecnicas/forms_tester/convencional.html +1 -1
tecnicas/constants.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ FORMS_TO_CREATE_SESSION = [
2
+ "form_basic",
3
+ "form_tags",
4
+ "form_codes",
5
+ "form_words"
6
+ ]
tecnicas/controllers/__init__.py CHANGED
@@ -12,8 +12,20 @@ from .models_controller.posicion_controller import PosicionController
12
  from .models_controller.particiapacion_controller import ParticipacionController
13
  from .models_controller.dato_controller import DatoController
14
 
15
- from .views_controller.detalles_sesion_controller import DetallesSesionController
16
  from .views_controller.login_tester_controller import LoginTesterController
17
  from .views_controller.main_tester_form_controller import MainTesterFormController
18
  from .views_controller.api_rating_controller import ApiRatingController
19
- from .views_controller.monitor_sesion_controller import MonitorSesionController
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  from .models_controller.particiapacion_controller import ParticipacionController
13
  from .models_controller.dato_controller import DatoController
14
 
 
15
  from .views_controller.login_tester_controller import LoginTesterController
16
  from .views_controller.main_tester_form_controller import MainTesterFormController
17
  from .views_controller.api_rating_controller import ApiRatingController
18
+ from .views_controller.tester_list_controller import TesterListController
19
+ from .views_controller.list_sessions_tester_controller import ListSessionsTesterController
20
+
21
+ from .views_controller.create_session.panel_basic_controller import PanelBasicController
22
+ from .views_controller.create_session.panel_tags_controller import PanelTagsController
23
+ from .views_controller.create_session.panel_codes_controller import PanelCodesController
24
+ from .views_controller.create_session.panel_words_controller import PanelWordsController
25
+ from .views_controller.create_session.panel_create_controller import PanelCreateController
26
+
27
+ from .views_controller.session_management.details_controller import DetallesController
28
+ from .views_controller.session_management.details_escala_controller import DetallesEscalasController
29
+ from .views_controller.session_management.details_rata_controller import DetallesRATAController
30
+ from .views_controller.session_management.monitor_controller import MonitorController
31
+ from .views_controller.session_management.monitor_escalas_controller import MonitorEscalasController
tecnicas/controllers/models_controller/calificacion_controller.py CHANGED
@@ -1,8 +1,7 @@
1
  from django.core.exceptions import ValidationError
2
- from django.db import DatabaseError
3
  from collections import defaultdict
4
- from ...models import Calificacion, Tecnica, Posicion, Producto, Catador
5
- from ...utils import controller_error, getId
6
 
7
 
8
  class CalificacionController():
@@ -46,7 +45,7 @@ class CalificacionController():
46
  repetition = technique.repeticion
47
 
48
  if not repetition:
49
- return {"error": "sin datos calficados aun"}
50
 
51
  ratings = list(Calificacion.objects.filter(id_tecnica=technique))
52
 
@@ -84,7 +83,7 @@ class CalificacionController():
84
  elif id_tester is not None:
85
  filters["id_catador__id"] = id_tester
86
  elif user_tester is not None:
87
- filters["id_catador__usuarioCatador"] = user_tester
88
 
89
  ratings = list(Calificacion.objects.filter(**filters).select_related(
90
  "id_producto",
 
1
  from django.core.exceptions import ValidationError
 
2
  from collections import defaultdict
3
+ from tecnicas.models import Calificacion, Tecnica, Posicion, Producto, Catador
4
+ from tecnicas.utils import controller_error, getId
5
 
6
 
7
  class CalificacionController():
 
45
  repetition = technique.repeticion
46
 
47
  if not repetition:
48
+ return controller_error("Sin datos calificados aún")
49
 
50
  ratings = list(Calificacion.objects.filter(id_tecnica=technique))
51
 
 
83
  elif id_tester is not None:
84
  filters["id_catador__id"] = id_tester
85
  elif user_tester is not None:
86
+ filters["id_catador__user__username"] = user_tester
87
 
88
  ratings = list(Calificacion.objects.filter(**filters).select_related(
89
  "id_producto",
tecnicas/controllers/models_controller/dato_controller.py CHANGED
@@ -89,7 +89,7 @@ class DatoController():
89
  producto_code=F(
90
  "id_dato__id_calificacion__id_producto__codigoProducto"),
91
  usuarioCatador=F(
92
- "id_dato__id_calificacion__id_catador__usuarioCatador"),
93
  dato_valor=F("valor")
94
  )
95
  )
 
89
  producto_code=F(
90
  "id_dato__id_calificacion__id_producto__codigoProducto"),
91
  usuarioCatador=F(
92
+ "id_dato__id_calificacion__id_catador__user__username"),
93
  dato_valor=F("valor")
94
  )
95
  )
tecnicas/controllers/models_controller/escala_controller.py CHANGED
@@ -26,7 +26,7 @@ class EscalaController():
26
  self.scale.save()
27
  return self.scale
28
  except DatabaseError as error:
29
- return controller_error("error al guardar la escala")
30
 
31
  def deleteScale(self):
32
  self.scale.delete()
@@ -67,7 +67,7 @@ class EscalaController():
67
  return self.tags_relation
68
  except DatabaseError as error:
69
  self.deleteRelationshipsWithLabels()
70
- return controller_error("error guardar relacion etiqueta escala")
71
 
72
  @staticmethod
73
  def getScaleByTechnique(technique: Tecnica = None, id_technique: int = None):
 
26
  self.scale.save()
27
  return self.scale
28
  except DatabaseError as error:
29
+ return controller_error("Error al guardar la escala")
30
 
31
  def deleteScale(self):
32
  self.scale.delete()
 
67
  return self.tags_relation
68
  except DatabaseError as error:
69
  self.deleteRelationshipsWithLabels()
70
+ return controller_error("Error en guardar la relación etiqueta escala")
71
 
72
  @staticmethod
73
  def getScaleByTechnique(technique: Tecnica = None, id_technique: int = None):
tecnicas/controllers/models_controller/ordenes_controller.py CHANGED
@@ -25,13 +25,13 @@ class OrdenesController():
25
 
26
  def saveOrders(self):
27
  if not self.orders:
28
- return controller_error("no se han establecido las ordenes para guardar")
29
  try:
30
  for order in self.orders:
31
  order.save()
32
  return self.orders
33
  except DatabaseError as error:
34
- return controller_error("error al guardar las ordenes")
35
 
36
  def setPositions(self):
37
  codes_ids_products = {}
@@ -40,21 +40,21 @@ class OrdenesController():
40
  codes_expect = list(codes_ids_products.keys())
41
 
42
  if len(self.orders) != len(self.raw_list_orders):
43
- return controller_error("el numero de ordenes guardados no coinciden con los recibidos")
44
 
45
  self.positions = []
46
  for index, order in enumerate(self.raw_list_orders):
47
  received_codes_order = list(order.keys())
48
 
49
  if set(received_codes_order) != set(codes_expect):
50
- return controller_error("las ordenes mandadas no contienen los productos esperados")
51
 
52
  for name, position_index in order.items():
53
  list_product_use = [product for product in self.products
54
  if product.codigoProducto == name]
55
 
56
  if len(list_product_use) != 1:
57
- return controller_error("no pueden existir dos productos que ocupen la misma posicion de un orden")
58
 
59
  product_use = list_product_use[0]
60
  new_position = Posicion(
@@ -67,13 +67,13 @@ class OrdenesController():
67
 
68
  def savePositions(self):
69
  if not self.positions:
70
- return controller_error("no se han establecido posiciones para guargar")
71
  try:
72
  for position in self.positions:
73
  position.save()
74
  return self.positions
75
  except DatabaseError as error:
76
- return controller_error("error al guardar las posiciones")
77
 
78
  @staticmethod
79
  def getOrderById(id: int):
 
25
 
26
  def saveOrders(self):
27
  if not self.orders:
28
+ return controller_error("No se han establecido las órdenes para guardar")
29
  try:
30
  for order in self.orders:
31
  order.save()
32
  return self.orders
33
  except DatabaseError as error:
34
+ return controller_error("Error al guardar las ordenes")
35
 
36
  def setPositions(self):
37
  codes_ids_products = {}
 
40
  codes_expect = list(codes_ids_products.keys())
41
 
42
  if len(self.orders) != len(self.raw_list_orders):
43
+ return controller_error("El número de ordenes guardados no coinciden con los recibidos")
44
 
45
  self.positions = []
46
  for index, order in enumerate(self.raw_list_orders):
47
  received_codes_order = list(order.keys())
48
 
49
  if set(received_codes_order) != set(codes_expect):
50
+ return controller_error("Las ordenes mandadas no contienen los productos esperados")
51
 
52
  for name, position_index in order.items():
53
  list_product_use = [product for product in self.products
54
  if product.codigoProducto == name]
55
 
56
  if len(list_product_use) != 1:
57
+ return controller_error("No pueden existir dos productos que ocupen la misma posición de un orden")
58
 
59
  product_use = list_product_use[0]
60
  new_position = Posicion(
 
67
 
68
  def savePositions(self):
69
  if not self.positions:
70
+ return controller_error("No se han establecido posiciones para guarguar")
71
  try:
72
  for position in self.positions:
73
  position.save()
74
  return self.positions
75
  except DatabaseError as error:
76
+ return controller_error("Error al guardar las posiciones")
77
 
78
  @staticmethod
79
  def getOrderById(id: int):
tecnicas/controllers/models_controller/palabras_controller.py CHANGED
@@ -14,11 +14,9 @@ class PalabrasController():
14
  self.ids_words = new_ids
15
 
16
  def setWords(self):
17
- self.words = []
18
- searched_words = list(Palabra.objects.filter(id__in=self.ids_words))
19
- if not len(searched_words):
20
- return controller_error("no se han encontrado registros")
21
- self.words = searched_words
22
  return self.words
23
 
24
  @staticmethod
 
14
  self.ids_words = new_ids
15
 
16
  def setWords(self):
17
+ self.words = list(Palabra.objects.filter(id__in=self.ids_words))
18
+ if not len(self.words):
19
+ return controller_error("No se han encontrado registros")
 
 
20
  return self.words
21
 
22
  @staticmethod
tecnicas/controllers/models_controller/particiapacion_controller.py CHANGED
@@ -1,12 +1,13 @@
1
- from ...models import Participacion, Tecnica, SesionSensorial
2
  from ...utils import controller_error
3
 
4
 
5
  class ParticipacionController():
6
  @staticmethod
7
- def enterSession(id_participation: int):
8
  try:
9
- participation = Participacion.objects.get(id=id_participation)
 
10
  participation.finalizado = False
11
  participation.activo = True
12
  participation.save()
@@ -26,9 +27,10 @@ class ParticipacionController():
26
  return controller_error("No se ha encontrado la participación")
27
 
28
  @staticmethod
29
- def outSession(id_participation: int):
30
  try:
31
- participation = Participacion.objects.get(id=id_participation)
 
32
  participation.activo = False
33
  participation.save()
34
  return participation
 
1
+ from ...models import Participacion, Tecnica, SesionSensorial, Catador
2
  from ...utils import controller_error
3
 
4
 
5
  class ParticipacionController():
6
  @staticmethod
7
+ def enterSession(tester: Catador, session: SesionSensorial):
8
  try:
9
+ participation = Participacion.objects.get(
10
+ catador=tester, tecnica=session.tecnica)
11
  participation.finalizado = False
12
  participation.activo = True
13
  participation.save()
 
27
  return controller_error("No se ha encontrado la participación")
28
 
29
  @staticmethod
30
+ def outSession(tester: Catador, session: SesionSensorial):
31
  try:
32
+ participation = Participacion.objects.get(
33
+ catador=tester, tecnica=session.tecnica)
34
  participation.activo = False
35
  participation.save()
36
  return participation
tecnicas/controllers/models_controller/sesion_controller.py CHANGED
@@ -18,9 +18,9 @@ class SesionController():
18
 
19
  def setSession(self):
20
  if not self.presenter:
21
- return controller_error("se requiere presentador para crear sesion")
22
  elif not self.technique:
23
- return controller_error("se requiere tecnica para crear sesion")
24
 
25
  self.sensorial_session = SesionSensorial(
26
  tecnica=self.technique,
@@ -34,22 +34,22 @@ class SesionController():
34
 
35
  def saveSession(self):
36
  if not self.sensorial_session:
37
- return controller_error("no se ha definido la sesion a guardar")
38
 
39
  try:
40
  self.sensorial_session.save()
41
  return self.sensorial_session
42
  except DatabaseError as error:
43
- return controller_error("Error al crear la session sensorial")
44
 
45
  @staticmethod
46
  def getSessionsSavesByCretor(user_name: str, page: int):
47
  elements_by_page = 6
48
 
49
  try:
50
- creator = Presentador.objects.get(nombre_usuario=user_name)
51
  except Presentador.DoesNotExist:
52
- return controller_error("presentador invalido")
53
 
54
  queryset = (
55
  SesionSensorial.objects
@@ -75,10 +75,14 @@ class SesionController():
75
  sessions_in_page = paginator.page(page)
76
  except PageNotAnInteger:
77
  return controller_error("índice inválido")
78
- except EmptyPage:
79
- return controller_error("sin registros de sesiones")
80
 
81
- return (sessions_in_page, not sessions_in_page.number < paginator.num_pages)
 
 
 
 
 
 
82
 
83
  @staticmethod
84
  def getSessionByCodePanelTester(code: str):
@@ -111,14 +115,14 @@ class SesionController():
111
  @staticmethod
112
  def getNumberSessionsByCreator(user_name: str):
113
  try:
114
- creator = Presentador.objects.get(nombre_usuario=user_name)
115
 
116
  number_sessions = SesionSensorial.objects.filter(
117
  creadoPor=creator).count()
118
 
119
  return number_sessions/9
120
  except Presentador.DoesNotExist:
121
- return controller_error("presentador invalido")
122
 
123
  @staticmethod
124
  def finishRepetion(session: SesionSensorial | str):
 
18
 
19
  def setSession(self):
20
  if not self.presenter:
21
+ return controller_error("Se requiere presentador para crear sesión")
22
  elif not self.technique:
23
+ return controller_error("Se requiere técnica para crear sesión")
24
 
25
  self.sensorial_session = SesionSensorial(
26
  tecnica=self.technique,
 
34
 
35
  def saveSession(self):
36
  if not self.sensorial_session:
37
+ return controller_error("No se ha definido la sesión a guardar")
38
 
39
  try:
40
  self.sensorial_session.save()
41
  return self.sensorial_session
42
  except DatabaseError as error:
43
+ return controller_error("Error al crear la sesión sensorial")
44
 
45
  @staticmethod
46
  def getSessionsSavesByCretor(user_name: str, page: int):
47
  elements_by_page = 6
48
 
49
  try:
50
+ creator = Presentador.objects.get(user__username=user_name)
51
  except Presentador.DoesNotExist:
52
+ return controller_error("Presentador invalido")
53
 
54
  queryset = (
55
  SesionSensorial.objects
 
75
  sessions_in_page = paginator.page(page)
76
  except PageNotAnInteger:
77
  return controller_error("índice inválido")
 
 
78
 
79
+ if not sessions_in_page.object_list:
80
+ return controller_error("Sin registros de sesiones")
81
+
82
+ current_page = sessions_in_page.number
83
+ is_last_page = not current_page < paginator.num_pages
84
+
85
+ return (sessions_in_page, is_last_page, current_page)
86
 
87
  @staticmethod
88
  def getSessionByCodePanelTester(code: str):
 
115
  @staticmethod
116
  def getNumberSessionsByCreator(user_name: str):
117
  try:
118
+ creator = Presentador.objects.get(user__username=user_name)
119
 
120
  number_sessions = SesionSensorial.objects.filter(
121
  creadoPor=creator).count()
122
 
123
  return number_sessions/9
124
  except Presentador.DoesNotExist:
125
+ return controller_error("Presentador invalido")
126
 
127
  @staticmethod
128
  def finishRepetion(session: SesionSensorial | str):
tecnicas/controllers/models_controller/tecnica_controller.py CHANGED
@@ -6,18 +6,18 @@ from tecnicas.utils import controller_error
6
  class TecnicaController():
7
  def setTechnique(self, **kwargs):
8
  self.technique = Tecnica(
9
- tipo_tecnica=kwargs["tipo_tecnica"],
10
- id_estilo=kwargs["estilo_palabras"],
11
- repeticiones_max=kwargs["numero_repeticiones"] or 0,
12
  limite_catadores=kwargs["numero_catadores"],
13
  instrucciones=kwargs["instrucciones"],
14
  )
15
 
16
  def setTechniqueFromBasicData(self, basic):
17
  self.technique = Tecnica(
18
- tipo_tecnica=TipoTecnica.objects.get(id=basic["id_tecnica"]),
19
  id_estilo=EstiloPalabra.objects.get(id=basic["estilo_palabras"]),
20
- repeticiones_max=basic["numero_repeticiones"] or 0,
21
  limite_catadores=basic["numero_catadores"],
22
  instrucciones=basic["instrucciones"] or "Espere instrucciones del Presentador",
23
  )
@@ -30,7 +30,7 @@ class TecnicaController():
30
  self.technique.save()
31
  return self.technique
32
  except DatabaseError:
33
- return controller_error("No se ha podido guardar la tecnica")
34
 
35
  def deleteTechnique(self):
36
  self.technique.delete()
 
6
  class TecnicaController():
7
  def setTechnique(self, **kwargs):
8
  self.technique = Tecnica(
9
+ tipo_tecnica=TipoTecnica.objects.get(nombre_tecnica=kwargs["name_tecnica"]),
10
+ id_estilo=EstiloPalabra.objects.get(id=kwargs["estilo_palabras"]),
11
+ repeticiones_max=kwargs["numero_repeticiones"] or 1,
12
  limite_catadores=kwargs["numero_catadores"],
13
  instrucciones=kwargs["instrucciones"],
14
  )
15
 
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",
23
  )
 
30
  self.technique.save()
31
  return self.technique
32
  except DatabaseError:
33
+ return controller_error("No se ha podido guardar la técnica")
34
 
35
  def deleteTechnique(self):
36
  self.technique.delete()
tecnicas/controllers/views_controller/create_session/panel_basic_controller.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.forms import SesionBasicForm
2
+ from django.http import HttpRequest
3
+ from django.shortcuts import redirect, render
4
+ from django.urls import reverse
5
+
6
+
7
+ class PanelBasicController():
8
+ conf_initial_rata = {
9
+ "numero_catadores": 0,
10
+ "numero_repeticiones": 1
11
+ }
12
+
13
+ def __init__(self):
14
+ pass
15
+
16
+ @staticmethod
17
+ def controllGetEscalas(request: HttpRequest):
18
+ form_sesion = SesionBasicForm()
19
+
20
+ view_context = {
21
+ "form_sesion": form_sesion,
22
+ "use_technique": "escalas"
23
+ }
24
+
25
+ response = render(
26
+ request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
27
+ return response
28
+
29
+ @staticmethod
30
+ def controllPostEscalas(request: HttpRequest, name_tecnica: str):
31
+ try:
32
+ form = SesionBasicForm(request.POST)
33
+
34
+ if form.is_valid():
35
+ values = {}
36
+ for name, value in form.cleaned_data.items():
37
+ if name == "estilo_palabras" or name == "tipo_escala":
38
+ values[name] = value.id
39
+ else:
40
+ values[name] = value
41
+
42
+ values["name_tecnica"] = name_tecnica
43
+ request.session['form_basic'] = values
44
+ response = redirect(
45
+ reverse("cata_system:panel_configuracion_tags"))
46
+ else:
47
+ response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
48
+ "form_sesion": form, "error": "Información no valida"})
49
+ except KeyError:
50
+ response = redirect(reverse(
51
+ "cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
52
+
53
+ return response
54
+
55
+ @staticmethod
56
+ def controllGetRATA(request: HttpRequest):
57
+ form_sesion = SesionBasicForm(
58
+ initial_conf=PanelBasicController.conf_initial_rata)
59
+
60
+ view_context = {
61
+ "form_sesion": form_sesion,
62
+ "use_technique": "rata"
63
+ }
64
+
65
+ response = render(
66
+ request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
67
+ return response
68
+
69
+ @staticmethod
70
+ def controllPostRATA(request: HttpRequest, name_tecnica: str):
71
+ try:
72
+ form = SesionBasicForm(
73
+ request.POST, initial_conf=PanelBasicController.conf_initial_rata)
74
+
75
+ if form.is_valid():
76
+ values = {}
77
+ for name, value in form.cleaned_data.items():
78
+ if name == "estilo_palabras" or name == "tipo_escala":
79
+ values[name] = value.id
80
+ else:
81
+ values[name] = value
82
+
83
+ for key, expected in PanelBasicController.conf_initial_rata.items():
84
+ actual = values.get(key)
85
+
86
+ if actual is None or str(actual) != str(expected):
87
+ form.add_error(
88
+ key, f"Valor inválido para '{key}': se esperaba {expected}, se recibió {actual}")
89
+
90
+ if form.errors:
91
+ response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
92
+ "form_sesion": form, "error": "No puedes modificar el número de catadores o repeticiones"})
93
+ else:
94
+ values["name_tecnica"] = name_tecnica
95
+ request.session['form_basic'] = values
96
+ response = redirect(
97
+ reverse("cata_system:panel_configuracion_tags"))
98
+ else:
99
+ response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
100
+ "form_sesion": form, "error": "Información no valida"})
101
+ except KeyError:
102
+ response = redirect(reverse(
103
+ "cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
104
+
105
+ return response
tecnicas/controllers/views_controller/create_session/panel_codes_controller.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.forms import CodesForm
5
+ from tecnicas.utils import generarCodigos
6
+ import json
7
+
8
+
9
+ class PanelCodesController():
10
+ def __init__(self):
11
+ pass
12
+
13
+ @staticmethod
14
+ def controllGetEscalas(request: HttpRequest, data):
15
+ (
16
+ num_products,
17
+ num_tester
18
+ ) = PanelCodesController.defineInfoConvencional(data)
19
+
20
+ codes_products = generarCodigos(num_products)
21
+
22
+ form_codes = CodesForm(codes=codes_products)
23
+
24
+ context_codes_form = {
25
+ "form_codes": form_codes,
26
+ "num_tester": num_tester,
27
+ "use_technique": "escalas"
28
+ }
29
+
30
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
31
+
32
+ @staticmethod
33
+ def controllPostEscalas(request: HttpRequest, data):
34
+ (
35
+ num_products,
36
+ num_tester
37
+ ) = PanelCodesController.defineInfoConvencional(data)
38
+
39
+ sorts_code = json.loads(request.POST.get("sort_codes"))
40
+ codes = []
41
+ context_codes_form = {}
42
+
43
+ for name, value in request.POST.items():
44
+ if name.__contains__("producto_"):
45
+ codes.append(value)
46
+
47
+ form_codes = CodesForm(request.POST, codes=codes)
48
+
49
+ context_codes_form = {
50
+ "form_codes": form_codes,
51
+ "num_tester": num_tester,
52
+ "use_technique": "escalas"
53
+ }
54
+
55
+ if form_codes.is_valid():
56
+ codes_sort = {"product_codes": []}
57
+
58
+ for name, value in form_codes.cleaned_data.items():
59
+ codes_sort["product_codes"].append({name: value})
60
+
61
+ codes_sort["sort_codes"] = sorts_code
62
+ request.session["form_codes"] = codes_sort
63
+ return redirect(reverse("cata_system:panel_configuracion_words"))
64
+ else:
65
+ context_codes_form["error"] = "error en los datos recibidos"
66
+
67
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
68
+
69
+ @staticmethod
70
+ def controllGetRATA(request: HttpRequest, data):
71
+ num_products = data["numero_productos"]
72
+ codes_products = generarCodigos(num_products)
73
+ form_codes = CodesForm(codes=codes_products)
74
+
75
+ context_codes_form = {
76
+ "form_codes": form_codes,
77
+ "num_tester": 0,
78
+ "use_technique": "rata"
79
+ }
80
+
81
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
82
+
83
+ @staticmethod
84
+ def controllPostRATA(request: HttpRequest):
85
+ codes = []
86
+ context_codes_form = {}
87
+
88
+ for name, value in request.POST.items():
89
+ if name.__contains__("producto_"):
90
+ codes.append(value)
91
+
92
+ form_codes = CodesForm(request.POST, codes=codes)
93
+
94
+ context_codes_form = {
95
+ "form_codes": form_codes,
96
+ "use_technique": "rata"
97
+ }
98
+
99
+ if form_codes.is_valid():
100
+ request.session["form_codes"] = codes
101
+ return redirect(reverse("cata_system:panel_configuracion_words"))
102
+ else:
103
+ context_codes_form["error"] = "error en los datos recibidos"
104
+
105
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
106
+
107
+ @staticmethod
108
+ def defineInfoConvencional(data):
109
+ num_products = data["numero_productos"]
110
+ num_tester = data["numero_catadores"]
111
+ return (
112
+ num_products,
113
+ num_tester
114
+ )
tecnicas/controllers/views_controller/create_session/panel_create_controller.py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, EstiloPalabrasController, PalabrasController, SesionController
6
+ from tecnicas.utils import deleteDataSession
7
+
8
+
9
+ class PanelCreateController():
10
+ def __init__(self):
11
+ pass
12
+
13
+ @staticmethod
14
+ def controllGetEscalas(request: HttpRequest):
15
+ return render(
16
+ request, 'tecnicas/create_sesion/creando_sesion.html')
17
+
18
+ @staticmethod
19
+ def controllPostEscalas(request: HttpRequest):
20
+ if request.POST.get('action') == 'create_session':
21
+ if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
22
+ deleteDataSession(request)
23
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
24
+
25
+ with transaction.atomic():
26
+ # ////////////////////////////////////////////////////// #
27
+ #
28
+ # First step: Create technique and scale with their tags #
29
+ #
30
+ # ////////////////////////////////////////////////////// #
31
+ data_basic = request.session["form_basic"]
32
+ controllerTechnique = TecnicaController()
33
+ controllerTechnique.setTechniqueFromBasicData(basic=data_basic)
34
+ technique = controllerTechnique.saveTechnique()
35
+ if not technique:
36
+ return general_error("Error al guardar la técnica")
37
+
38
+ data_scale = {
39
+ "id_scale": data_basic["tipo_escala"],
40
+ "size": data_basic["tamano_escala"],
41
+ "technique": technique
42
+ }
43
+
44
+ controllerScale = EscalaController(data=data_scale)
45
+
46
+ scale = controllerScale.saveScale()
47
+ if isinstance(scale, dict):
48
+ return general_error(scale["error"])
49
+
50
+ dict_tags = request.session["form_tags"]
51
+ saved_related_tags = controllerScale.realteTags(dict_tags)
52
+ if "error" in saved_related_tags:
53
+ return general_error(saved_related_tags["error"])
54
+
55
+ # ////////////////////////////////////////////////////////// #
56
+ #
57
+ # Second step: Create orders, productos and set the position #
58
+ #
59
+ # ////////////////////////////////////////////////////////// #
60
+ data_codes = request.session["form_codes"]
61
+
62
+ list_codes_dict = data_codes["product_codes"]
63
+
64
+ codes = []
65
+ for product in list_codes_dict:
66
+ code = next(iter(product.values()))
67
+ codes.append(code)
68
+
69
+ controllerProducts = ProductosController(
70
+ codes=codes,
71
+ technique=technique
72
+ )
73
+
74
+ controllerProducts.setProductsNoSave()
75
+ saved_prodcuts = controllerProducts.saveProducts()
76
+ if isinstance(saved_prodcuts, dict):
77
+ return general_error(saved_prodcuts["error"])
78
+
79
+ raw_sort_codes = data_codes["sort_codes"]
80
+ controllerOrdes = OrdenesController(
81
+ raw_orders=raw_sort_codes,
82
+ list_products=saved_prodcuts,
83
+ technique=technique
84
+ )
85
+
86
+ controllerOrdes.setOrdersToSave()
87
+ saved_orders = controllerOrdes.saveOrders()
88
+ if isinstance(saved_orders, dict):
89
+ return general_error(saved_orders["error"])
90
+
91
+ seded_positions = controllerOrdes.setPositions()
92
+ if isinstance(seded_positions, dict):
93
+ return general_error(seded_positions["error"])
94
+
95
+ saved_postions = controllerOrdes.savePositions()
96
+ if isinstance(saved_postions, dict):
97
+ return general_error(saved_prodcuts["error"])
98
+
99
+ # /////////////////////////////////////////////////////// #
100
+ #
101
+ # Third step: Create relations technique with Words Style #
102
+ #
103
+ # /////////////////////////////////////////////////////// #
104
+ ids_words = request.session["form_words"]
105
+ words_controller = PalabrasController(ids=ids_words)
106
+
107
+ words_to_use = words_controller.setWords()
108
+ if isinstance(words_to_use, dict):
109
+ return general_error(words_to_use["error"])
110
+
111
+ style_controller = EstiloPalabrasController(
112
+ technique=technique, words=words_to_use)
113
+
114
+ instace_style = style_controller.createAndSaveInstaceStyle()
115
+ if isinstance(instace_style, dict):
116
+ return general_error(instace_style["error"])
117
+
118
+ words_using = style_controller.relatedWords()
119
+ if isinstance(words_using, dict):
120
+ return general_error(words_using["error"])
121
+
122
+ # //////////////////////////////////////////////////////// #
123
+ #
124
+ # Fourth step: Create session and relat with the technique #
125
+ #
126
+ # //////////////////////////////////////////////////////// #
127
+ session_controller = SesionController(
128
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
129
+ technique=technique,
130
+ creator=request.user.user_presentador
131
+ )
132
+
133
+ setting_session = session_controller.setSession()
134
+ if isinstance(setting_session, dict):
135
+ return general_error(setting_session["error"])
136
+
137
+ saved_session = session_controller.saveSession()
138
+ if isinstance(saved_session, dict):
139
+ return general_error(saved_session["error"])
140
+
141
+ context = {
142
+ "message": "sesión creada",
143
+ "data": {
144
+ "codigo_sesion": saved_session.codigo_sesion,
145
+ "nombre_sesion": saved_session.nombre_sesion
146
+ }
147
+ }
148
+
149
+ # ////////////////////////////////// #
150
+ #
151
+ # Final step: Delete date in session #
152
+ #
153
+ # ////////////////////////////////// #
154
+
155
+ deleteDataSession(request)
156
+ return JsonResponse(context)
157
+ else:
158
+ return general_error("No se ha establecido acción")
159
+
160
+ @staticmethod
161
+ def controllPostRATA(request: HttpRequest):
162
+ if request.POST.get('action') == 'create_session':
163
+ if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
164
+ deleteDataSession(request)
165
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
166
+
167
+ with transaction.atomic():
168
+ # ////////////////////////////////////////////////////// #
169
+ #
170
+ # First step: Create technique and scale with their tags #
171
+ #
172
+ # ////////////////////////////////////////////////////// #
173
+ data_basic = request.session["form_basic"]
174
+ data_basic["numero_catadores"] = 0
175
+ data_basic["numero_repeticiones"] = 1
176
+ controllerTechnique = TecnicaController()
177
+ controllerTechnique.setTechniqueFromBasicData(basic=data_basic)
178
+ technique = controllerTechnique.saveTechnique()
179
+ if not technique:
180
+ return general_error("Error al guardar la técnica")
181
+
182
+ data_scale = {
183
+ "id_scale": data_basic["tipo_escala"],
184
+ "size": data_basic["tamano_escala"],
185
+ "technique": technique
186
+ }
187
+
188
+ controllerScale = EscalaController(data=data_scale)
189
+
190
+ scale = controllerScale.saveScale()
191
+ if isinstance(scale, dict):
192
+ return general_error(scale["error"])
193
+
194
+ dict_tags = request.session["form_tags"]
195
+ saved_related_tags = controllerScale.realteTags(dict_tags)
196
+ if "error" in saved_related_tags:
197
+ return general_error(saved_related_tags["error"])
198
+
199
+ # ////////////////////////////////////////////// #
200
+ #
201
+ # Second step: Create productos with their codes #
202
+ #
203
+ # ////////////////////////////////////////////// #
204
+ codes = request.session["form_codes"]
205
+
206
+ controllerProducts = ProductosController(
207
+ codes=codes,
208
+ technique=technique
209
+ )
210
+
211
+ controllerProducts.setProductsNoSave()
212
+ saved_prodcuts = controllerProducts.saveProducts()
213
+ if isinstance(saved_prodcuts, dict):
214
+ return general_error(saved_prodcuts["error"])
215
+
216
+ # /////////////////////////////////////////////////////// #
217
+ #
218
+ # Third step: Create relations technique with Words Style #
219
+ #
220
+ # /////////////////////////////////////////////////////// #
221
+ ids_words = request.session["form_words"]
222
+ words_controller = PalabrasController(ids=ids_words)
223
+
224
+ words_to_use = words_controller.setWords()
225
+ if isinstance(words_to_use, dict):
226
+ return general_error(words_to_use["error"])
227
+
228
+ style_controller = EstiloPalabrasController(
229
+ technique=technique, words=words_to_use)
230
+
231
+ instace_style = style_controller.createAndSaveInstaceStyle()
232
+ if isinstance(instace_style, dict):
233
+ return general_error(instace_style["error"])
234
+
235
+ words_using = style_controller.relatedWords()
236
+ if isinstance(words_using, dict):
237
+ return general_error(words_using["error"])
238
+
239
+ # //////////////////////////////////////////////////////// #
240
+ #
241
+ # Fourth step: Create session and relat with the technique #
242
+ #
243
+ # //////////////////////////////////////////////////////// #
244
+ session_controller = SesionController(
245
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
246
+ technique=technique,
247
+ creator=request.user.user_presentador
248
+ )
249
+
250
+ setting_session = session_controller.setSession()
251
+ if isinstance(setting_session, dict):
252
+ return general_error(setting_session["error"])
253
+
254
+ saved_session = session_controller.saveSession()
255
+ if isinstance(saved_session, dict):
256
+ return general_error(saved_session["error"])
257
+
258
+ context = {
259
+ "message": "sesión creada",
260
+ "data": {
261
+ "codigo_sesion": saved_session.codigo_sesion,
262
+ "nombre_sesion": saved_session.nombre_sesion
263
+ }
264
+ }
265
+
266
+ # ////////////////////////////////// #
267
+ #
268
+ # Final step: Delete date en session #
269
+ #
270
+ # ////////////////////////////////// #
271
+ deleteDataSession(request)
272
+ return JsonResponse(context)
273
+ else:
274
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panel_tags_controller.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.forms import SesionTagsForm, EtiquetaForm
5
+ from tecnicas.models import TipoEscala
6
+
7
+
8
+ class PanelTagsController():
9
+ def __init__(self):
10
+ pass
11
+
12
+ @staticmethod
13
+ def controllGetEscalas(request: HttpRequest, data):
14
+ (
15
+ type_scale,
16
+ tamano_escala,
17
+ form_new_etiqueta
18
+ ) = PanelTagsController.defineInfoScale(data)
19
+
20
+ form_etiqutas = SesionTagsForm(
21
+ longitud=tamano_escala, tipo_escala=type_scale.nombre_escala)
22
+
23
+ context_tags = {
24
+ "form_tags": form_etiqutas,
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):
32
+ (
33
+ type_scale,
34
+ tamano_escala,
35
+ form_new_etiqueta
36
+ ) = PanelTagsController.defineInfoScale(data)
37
+
38
+ values = {}
39
+ form = SesionTagsForm(request.POST, longitud=tamano_escala,
40
+ tipo_escala=type_scale.nombre_escala)
41
+
42
+ context_tags = {
43
+ "form_tags": form,
44
+ "form_new_tag": form_new_etiqueta
45
+ }
46
+
47
+ if form.is_valid():
48
+ for name, value in form.cleaned_data.items():
49
+ values[name] = value.id
50
+
51
+ request.session["form_tags"] = values
52
+ response = redirect(
53
+ reverse("cata_system:panel_configuracion_codes"))
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
+
61
+ @staticmethod
62
+ def defineInfoScale(data):
63
+ type_scale = TipoEscala.objects.get(pk=data["tipo_escala"])
64
+ tamano_escala = data["tamano_escala"]
65
+ form_new_etiqueta = EtiquetaForm()
66
+
67
+ return (
68
+ type_scale,
69
+ tamano_escala,
70
+ form_new_etiqueta
71
+ )
tecnicas/controllers/views_controller/create_session/panel_words_controller.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from tecnicas.forms import WordForm
3
+ from django.shortcuts import render, redirect
4
+ from django.urls import reverse
5
+ from tecnicas.models import Palabra
6
+ import json
7
+
8
+
9
+ class PanelWordsController():
10
+ def __init__(self):
11
+ pass
12
+
13
+ @staticmethod
14
+ def controllGetEscalas(request: HttpRequest):
15
+ form = WordForm()
16
+ context = {
17
+ "form_word": form
18
+ }
19
+
20
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
21
+
22
+ @staticmethod
23
+ def controllPostEscalas(request: HttpRequest):
24
+ form = WordForm()
25
+ context = {
26
+ "form_word": form
27
+ }
28
+
29
+ if not request.POST.get("words"):
30
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
31
+
32
+ words = json.loads(request.POST.get("words"))
33
+ context["words"] = words
34
+
35
+ ids_words = [word["id"] for word in words]
36
+
37
+ if len(ids_words) != len(set(ids_words)):
38
+ context["error"] = "existen palabras duplicadas"
39
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
40
+
41
+ exist_words = Palabra.objects.filter(
42
+ id__in=ids_words).count() == len(ids_words)
43
+
44
+ if not exist_words:
45
+ context["error"] = "algunas palabras no existen"
46
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
47
+
48
+ request.session["form_words"] = ids_words
49
+ return redirect(reverse("cata_system:creando_sesion"))
tecnicas/controllers/views_controller/list_sessions_tester_controller.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.core.paginator import Paginator, PageNotAnInteger
2
+ from django.db.models import OuterRef, Subquery, BooleanField
3
+ from tecnicas.models import Catador, SesionSensorial, Participacion
4
+ from tecnicas.utils import controller_error
5
+
6
+
7
+ class ListSessionsTesterController():
8
+ def __init__(self):
9
+ pass
10
+
11
+ def getContext(self, user: Catador, page: int):
12
+ context_view = {}
13
+
14
+ response = ListSessionsTesterController.getSessionByTester(page, user)
15
+ if isinstance(response, dict):
16
+ return response
17
+
18
+ (sessions, is_last_page, current_page) = response
19
+ context_view["sessions"] = sessions
20
+ context_view["last_page"] = is_last_page
21
+ context_view["page"] = current_page
22
+
23
+ return context_view
24
+
25
+ @staticmethod
26
+ def getSessionByTester(page: int, tester: Catador):
27
+ elements_by_page = 6
28
+
29
+ base_qs = SesionSensorial.objects.filter(
30
+ tecnica__tecnica_participacion__catador=tester
31
+ )
32
+
33
+ participacion_finalizado_subq = Subquery(
34
+ Participacion.objects.filter(
35
+ tecnica=OuterRef('tecnica_id'),
36
+ catador=tester
37
+ ).values('finalizado')[:1],
38
+ output_field=BooleanField()
39
+ )
40
+
41
+ participacion_activo_subq = Subquery(
42
+ Participacion.objects.filter(
43
+ tecnica=OuterRef('tecnica_id'),
44
+ catador=tester
45
+ ).values('activo')[:1],
46
+ output_field=BooleanField()
47
+ )
48
+
49
+ queryset = (
50
+ base_qs
51
+ .select_related(
52
+ 'tecnica',
53
+ 'tecnica__tipo_tecnica',
54
+ 'tecnica__id_estilo',
55
+ )
56
+ .annotate(
57
+ participacion_finalizado=participacion_finalizado_subq,
58
+ participacion_activo=participacion_activo_subq,
59
+ )
60
+ .order_by('participacion_finalizado', '-activo', '-fechaCreacion')
61
+ .distinct()
62
+ )
63
+
64
+ paginator = Paginator(queryset, elements_by_page)
65
+ try:
66
+ sessions_in_page = paginator.page(page)
67
+ except PageNotAnInteger:
68
+ return controller_error("índice inválido")
69
+
70
+ if not sessions_in_page.object_list:
71
+ return controller_error("Sin registros de Participaciones")
72
+
73
+ current_page = sessions_in_page.number
74
+ is_last_page = not current_page < paginator.num_pages
75
+
76
+ return (sessions_in_page, is_last_page, current_page)
tecnicas/controllers/views_controller/login_tester_controller.py CHANGED
@@ -1,12 +1,17 @@
1
- from ...models import Catador, SesionSensorial, Participacion
2
- from ...utils import controller_error
 
3
  from django.db import transaction
 
 
4
 
5
 
6
  class LoginTesterController():
7
  tester: Catador
8
  session: SesionSensorial
9
  taster_participation: Participacion
 
 
10
 
11
  def __init__(self):
12
  self.tester = Catador()
@@ -14,7 +19,7 @@ class LoginTesterController():
14
 
15
  def existCredential(self, user_tester: str, code_session: str):
16
  try:
17
- self.tester = Catador.objects.get(usuarioCatador=user_tester)
18
  self.session = SesionSensorial.objects.get(
19
  codigo_sesion=code_session)
20
 
@@ -22,42 +27,73 @@ class LoginTesterController():
22
  except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
23
  return controller_error("Credenciales inválidas")
24
 
25
- def validateEntry(self):
26
- if not self.tester.nombre or not self.session.codigo_sesion:
27
- return controller_error("Credenciales no definidas")
28
-
29
  if not self.session.activo:
30
- return controller_error("La sesión no está activa actualmente")
 
31
 
32
  if self.session.tecnica.repeticion > 1:
33
  try:
34
  self.taster_participation = Participacion.objects.get(
35
  tecnica=self.session.tecnica, catador=self.tester)
36
- return self.taster_participation
 
37
  except Participacion.DoesNotExist:
38
- return controller_error("No tienes permitido entrar a esta sesión")
 
39
  else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  try:
41
  self.taster_participation = Participacion.objects.get(
42
  tecnica=self.session.tecnica, catador=self.tester)
43
- return self.taster_participation
 
44
  except Participacion.DoesNotExist:
45
  with transaction.atomic():
46
  code_session = self.session.codigo_sesion
47
  self.session = SesionSensorial.objects.select_for_update().get(
48
  codigo_sesion=code_session)
49
 
50
- max_testers = self.session.tecnica.limite_catadores
51
- current_num_testers = Participacion.objects.filter(
52
- tecnica=self.session.tecnica).count()
53
-
54
- if current_num_testers >= max_testers:
55
- return controller_error("La sesión ha alcanzado el número máximo de catadores")
56
-
57
  self.taster_participation = Participacion.objects.create(
58
  tecnica=self.session.tecnica,
59
  catador=self.tester,
60
  finalizado=False
61
  )
62
 
63
- return self.taster_participation
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest, JsonResponse
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
  from django.db import transaction
5
+ from tecnicas.models import Catador, SesionSensorial, Participacion
6
+ from tecnicas.utils import controller_error
7
 
8
 
9
  class LoginTesterController():
10
  tester: Catador
11
  session: SesionSensorial
12
  taster_participation: Participacion
13
+ current_direcction = "tecnicas/forms_tester/login_session.html"
14
+ destinity_direcction = "cata_system:catador_init_session"
15
 
16
  def __init__(self):
17
  self.tester = Catador()
 
19
 
20
  def existCredential(self, user_tester: str, code_session: str):
21
  try:
22
+ self.tester = Catador.objects.get(user__username=user_tester)
23
  self.session = SesionSensorial.objects.get(
24
  codigo_sesion=code_session)
25
 
 
27
  except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
28
  return controller_error("Credenciales inválidas")
29
 
30
+ def validateEntryEscalas(self, request=HttpRequest):
31
+ context = {}
 
 
32
  if not self.session.activo:
33
+ context["error"] = "La sesión no está activa actualmente"
34
+ return render(request, self.current_direcction, context)
35
 
36
  if self.session.tecnica.repeticion > 1:
37
  try:
38
  self.taster_participation = Participacion.objects.get(
39
  tecnica=self.session.tecnica, catador=self.tester)
40
+ context["error"] = "Usted ya esta dentro de la sesión"
41
+ return render(request, self.current_direcction, context)
42
  except Participacion.DoesNotExist:
43
+ context["error"] = "No tienes permitido entrar a esta sesión"
44
+ return render(request, self.current_direcction, context)
45
  else:
46
+ with transaction.atomic():
47
+ code_session = self.session.codigo_sesion
48
+ self.session = SesionSensorial.objects.select_for_update().get(
49
+ codigo_sesion=code_session)
50
+
51
+ max_testers = self.session.tecnica.limite_catadores
52
+ current_num_testers = Participacion.objects.filter(
53
+ tecnica=self.session.tecnica).count()
54
+
55
+ if current_num_testers >= max_testers:
56
+ context["error"] = "La sesión ha alcanzado el número máximo de catadores"
57
+ return render(request, self.current_direcction, context)
58
+
59
+ self.taster_participation = Participacion.objects.create(
60
+ tecnica=self.session.tecnica,
61
+ catador=self.tester,
62
+ finalizado=False
63
+ )
64
+ params = {
65
+ "code_sesion": self.session.codigo_sesion
66
+ }
67
+ return redirect(reverse(self.destinity_direcction, kwargs=params))
68
+
69
+ def validateEntryRATA(self, request: HttpRequest):
70
+ context = {}
71
+ if not self.session.activo:
72
+ context["error"] = "La sesión no está activa actualmente"
73
+ return render(request, self.current_direcction, context)
74
+
75
+ if self.session.tecnica.repeticion <= 1:
76
  try:
77
  self.taster_participation = Participacion.objects.get(
78
  tecnica=self.session.tecnica, catador=self.tester)
79
+ context["error"] = "Usted ya esta dentro de la sesión"
80
+ return render(request, self.current_direcction, context)
81
  except Participacion.DoesNotExist:
82
  with transaction.atomic():
83
  code_session = self.session.codigo_sesion
84
  self.session = SesionSensorial.objects.select_for_update().get(
85
  codigo_sesion=code_session)
86
 
 
 
 
 
 
 
 
87
  self.taster_participation = Participacion.objects.create(
88
  tecnica=self.session.tecnica,
89
  catador=self.tester,
90
  finalizado=False
91
  )
92
 
93
+ params = {
94
+ "code_sesion": self.session.codigo_sesion
95
+ }
96
+ return redirect(reverse(self.destinity_direcction, kwargs=params))
97
+ else:
98
+ context["error"] = "Imposible acceder a esta sesión"
99
+ return render(request, self.current_direcction, context)
tecnicas/controllers/views_controller/main_tester_form_controller.py CHANGED
@@ -10,7 +10,7 @@ class MainTesterFormController():
10
 
11
  def __init__(self, code_session: str, user_tester: str):
12
  try:
13
- self.tester = Catador.objects.get(usuarioCatador=user_tester)
14
  self.session = SesionSensorial.objects.get(
15
  codigo_sesion=code_session)
16
  except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
@@ -21,6 +21,8 @@ class MainTesterFormController():
21
  orders_without_tester = list(Orden.objects.select_for_update().filter(
22
  id_tecnica=self.session.tecnica, id_catador=None))
23
 
 
 
24
  if not orders_without_tester:
25
  return controller_error("Las ordenes se han acabado")
26
 
@@ -32,21 +34,20 @@ class MainTesterFormController():
32
 
33
  return self.order_to_assign
34
 
35
- def checkAssignOrder(self):
36
- if not self.tester or not self.session:
37
- return controller_error("Atributos no establecidos")
38
-
39
  try:
40
- res_order = Orden.objects.get(
41
  id_tecnica=self.session.tecnica, id_catador=self.tester)
42
- self.order = res_order
43
- return self.order
44
  except Orden.DoesNotExist:
45
- return controller_error("Catador sin orden")
 
 
 
46
 
47
- def isEndedSession(self, id_participation: int, repetition: int):
48
  try:
49
- participation = Participacion.objects.get(id=id_participation)
 
50
 
51
  # ////////////////////////////////////////////////////////////// #
52
  #
@@ -76,7 +77,7 @@ class MainTesterFormController():
76
 
77
  expected_ratings_repetition = num_products * num_words
78
 
79
- return num_ratings_now >= expected_ratings_repetition
80
  else:
81
  return participation.finalizado
82
  except Participacion.DoesNotExist:
 
10
 
11
  def __init__(self, code_session: str, user_tester: str):
12
  try:
13
+ self.tester = Catador.objects.get(user__username=user_tester)
14
  self.session = SesionSensorial.objects.get(
15
  codigo_sesion=code_session)
16
  except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
 
21
  orders_without_tester = list(Orden.objects.select_for_update().filter(
22
  id_tecnica=self.session.tecnica, id_catador=None))
23
 
24
+ print(orders_without_tester)
25
+
26
  if not orders_without_tester:
27
  return controller_error("Las ordenes se han acabado")
28
 
 
34
 
35
  return self.order_to_assign
36
 
37
+ def checkAndAssignOrder(self):
 
 
 
38
  try:
39
+ self.order_to_assign = Orden.objects.get(
40
  id_tecnica=self.session.tecnica, id_catador=self.tester)
 
 
41
  except Orden.DoesNotExist:
42
+ create = self.assignOrder()
43
+ if isinstance(create, dict):
44
+ return create
45
+ return self.order_to_assign
46
 
47
+ def isEndedSession(self, repetition: int):
48
  try:
49
+ participation = Participacion.objects.get(
50
+ catador=self.tester, tecnica=self.session.tecnica)
51
 
52
  # ////////////////////////////////////////////////////////////// #
53
  #
 
77
 
78
  expected_ratings_repetition = num_products * num_words
79
 
80
+ return num_ratings_now >= expected_ratings_repetition
81
  else:
82
  return participation.finalizado
83
  except Participacion.DoesNotExist:
tecnicas/controllers/views_controller/session_management/details_controller.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import SesionSensorial, Presentador, Tecnica
2
+ from tecnicas.utils import controller_error
3
+
4
+
5
+ class DetallesController():
6
+ url_template = "tecnicas/manage_sesions/detalles-sesion.html"
7
+
8
+ def __init__(self, session: SesionSensorial):
9
+ self.session = session
10
+
11
+ def deleteSesorialSession(self):
12
+ technique = Tecnica.objects.get(id=self.session.tecnica.id)
13
+ technique.delete()
tecnicas/controllers/views_controller/{detalles_sesion_controller.py → session_management/details_escala_controller.py} RENAMED
@@ -11,29 +11,42 @@ Encabezados de como deben de aparecer los datos juntos
11
  | Repeticion | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
12
 
13
  '''
14
-
15
- from ...models import SesionSensorial, Presentador, Tecnica, Palabra
16
- from .. import CalificacionController, PalabrasController
17
- from ...utils import controller_error
 
 
 
18
  from collections import defaultdict
19
- from tecnicas.controllers import DatoController
20
- from tecnicas.utils import defaultdict_to_dict
21
 
22
 
23
- class DetallesSesionController():
24
- def __init__(self, session_code: str):
25
- self.session = SesionSensorial.objects.get(codigo_sesion=session_code)
26
 
27
- def getContextForView(self):
28
- self.context = {}
29
 
30
- self.context["sesion"] = self.session
 
 
 
 
 
 
 
 
 
31
 
 
 
 
 
 
32
  self.words = PalabrasController.getWordsInTechnique(
33
  self.session.tecnica)
34
  self.context["palabras"] = [word.nombre_palabra for word in self.words]
35
 
36
- def getContextWithData(self):
37
  ratings_for_repetition = []
38
 
39
  ratings = CalificacionController.getRatingsByTechnique(
@@ -66,30 +79,25 @@ class DetallesSesionController():
66
 
67
  return self.context
68
 
69
- @staticmethod
70
- def startRepetition(session_code: str, username: str):
71
- try:
72
- creator = Presentador.objects.get(nombre_usuario=username)
73
- session = SesionSensorial.objects.get(codigo_sesion=session_code)
74
- technique = Tecnica.objects.get(id=session.tecnica.id)
75
- except Presentador.DoesNotExist:
76
- return controller_error("no existe presentador")
77
- except SesionSensorial.DoesNotExist:
78
- return controller_error("no existe sesión sensorial")
79
- except Tecnica.DoesNotExist:
80
- return controller_error("Ha ocurrido un error al recuperar la técnica")
81
-
82
- if creator.nombre_usuario != session.creadoPor.nombre_usuario:
83
- return controller_error("solo el presentador que crea la sesión puede iniciar la repetición")
84
- elif session.activo:
85
- return controller_error("la sesión ya está activada")
86
  elif technique.repeticion == technique.repeticiones_max:
87
- return controller_error("se ha alcanzado el número de repeticiones máxima")
88
 
89
- session.activo = True
90
  technique.repeticion = technique.repeticion + 1
91
 
92
  technique.save()
93
- session.save()
94
 
95
- return session
 
 
 
 
 
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, Tecnica
18
+ from tecnicas.controllers import DatoController, CalificacionController, PalabrasController
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
+ url_template = "tecnicas/manage_sesions/detalles-sesion.html"
 
26
 
27
+ def __init__(self, session: SesionSensorial):
28
+ super().__init__(session)
29
 
30
+ def getResponse(self, request: HttpRequest, error: str = "", message: str = ""):
31
+ context = self.getContext()
32
+
33
+ if error != "" or error:
34
+ context["error"] = error
35
+ if message != "" or message:
36
+ context["message"] = message
37
+
38
+ return render(
39
+ request, self.url_template, context)
40
 
41
+ def getContext(self):
42
+ self.context = {
43
+ "use_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
44
+ }
45
+ self.context["sesion"] = self.session
46
  self.words = PalabrasController.getWordsInTechnique(
47
  self.session.tecnica)
48
  self.context["palabras"] = [word.nombre_palabra for word in self.words]
49
 
 
50
  ratings_for_repetition = []
51
 
52
  ratings = CalificacionController.getRatingsByTechnique(
 
79
 
80
  return self.context
81
 
82
+ def startRepetition(self, presenter: Presentador):
83
+ creator = presenter
84
+ technique = self.session.tecnica
85
+
86
+ if creator.user.username != self.session.creadoPor.user.username:
87
+ return self.getResponse(error="Solo el presentador que crea la sesión puede iniciar la repetición")
88
+ elif self.session.activo:
89
+ return self.getResponse(error="La sesión ya está activada")
 
 
 
 
 
 
 
 
 
90
  elif technique.repeticion == technique.repeticiones_max:
91
+ return self.getResponse(error="Se ha alcanzado el número de repeticiones máxima")
92
 
93
+ self.session.activo = True
94
  technique.repeticion = technique.repeticion + 1
95
 
96
  technique.save()
97
+ self.session.save()
98
 
99
+ parameters = {
100
+ "session_code": self.session.codigo_sesion
101
+ }
102
+ return redirect(
103
+ reverse("cata_system:monitor_sesion", kwargs=parameters))
tecnicas/controllers/views_controller/session_management/details_rata_controller.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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):
11
+ def __init__(self, session: SesionSensorial):
12
+ super().__init__(session)
13
+
14
+ def getResponse(self, request: HttpRequest, error: str = ""):
15
+ context = self.getContext()
16
+ if error != "" or error:
17
+ context["error"] = error
18
+ return render(
19
+ request, self.url_template, context)
20
+
21
+ def getContext(self):
22
+ self.context = {}
23
+ self.context["sesion"] = self.session
24
+ self.words = PalabrasController.getWordsInTechnique(
25
+ self.session.tecnica)
26
+ self.context["palabras"] = [word.nombre_palabra for word in self.words]
27
+
28
+ ratings_for_repetition = []
29
+
30
+ ratings = CalificacionController.getRatingsByTechnique(
31
+ technique=self.session.tecnica)
32
+
33
+ if isinstance(ratings, dict) or not ratings:
34
+ self.context["calificaciones"] = ratings_for_repetition
35
+ self.context["existen_calificaciones"] = False
36
+ return self.context
37
+
38
+ data = DatoController.getWordValuesForConvecional(
39
+ ratings=ratings, technique=self.session.tecnica)
40
+
41
+ ratings_for_repetition = defaultdict(
42
+ lambda: defaultdict(lambda: defaultdict(list)))
43
+
44
+ for item in data:
45
+ user = item["usuarioCatador"]
46
+ rep = item["repeticion"]
47
+ prod = item["producto_code"]
48
+
49
+ ratings_for_repetition[rep][user][prod].append({
50
+ "nombre_palabra": item["nombre_palabra"],
51
+ "dato_valor": item["dato_valor"]
52
+ })
53
+
54
+ self.context["calificaciones"] = defaultdict_to_dict(
55
+ ratings_for_repetition)
56
+ self.context["existen_calificaciones"] = True
tecnicas/controllers/views_controller/session_management/monitor_controller.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import SesionSensorial
2
+
3
+
4
+ class MonitorController():
5
+ def __init__(self, session: SesionSensorial):
6
+ self.sensorial_session = session
7
+
8
+ def updataSession(self):
9
+ self.sensorial_session = SesionSensorial.objects.get(
10
+ codigo_sesion=self.sensorial_session.codigo_sesion)
tecnicas/controllers/views_controller/{monitor_sesion_controller.py → session_management/monitor_escalas_controller.py} RENAMED
@@ -1,61 +1,72 @@
 
 
 
1
  from tecnicas.models import SesionSensorial, EsAtributo, EsVocabulario, Producto, Calificacion
2
  from tecnicas.controllers import ParticipacionController, SesionController
3
  from tecnicas.utils import controller_error
 
4
 
5
 
6
- class MonitorSesionController():
7
- def __init__(self, session_code: str):
8
- self.code_session = session_code
9
 
10
- def defineSession(self):
11
- self.session = SesionSensorial.objects.get(
12
- codigo_sesion=self.code_session)
13
 
14
- def monitorView(self):
15
- try:
16
- self.sensorial_session = SesionSensorial.objects.select_related(
17
- "tecnica"
18
- ).only(
19
- "nombre_sesion",
20
- "tecnica__limite_catadores",
21
- ).get(codigo_sesion=self.code_session)
22
 
23
- self.participations = ParticipacionController.getParticipationsInTechinique(
24
- self.sensorial_session.tecnica)
25
- except SesionSensorial.DoesNotExist as error:
26
- return controller_error("No existe Sesión sensorial")
27
 
28
- context = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  "session_name": self.sensorial_session.nombre_sesion,
30
  "max_testers": self.sensorial_session.tecnica.limite_catadores,
31
  "current_testers": len(self.participations),
32
  "active_testers": len([part for part in self.participations if part.activo]),
33
- "participations": self.participations
 
34
  }
35
 
36
- return context
37
-
38
  def getExpectedRatings(self):
39
  num_products = Producto.objects.filter(
40
- id_tecnica=self.session.tecnica).count()
41
-
42
- style_words = self.session.tecnica.id_estilo
43
-
44
  num_words: int
45
 
46
  if style_words.nombre_estilo == "atributos":
47
  num_words = EsAtributo.objects.get(
48
- id_tecnica=self.session.tecnica).palabras.count()
49
  elif style_words.nombre_estilo == "vocabulario":
50
  num_words = EsVocabulario.objects.get(
51
- id_tecnica=self.session.tecnica).id_vocabulario.palabras.count()
52
 
53
  return num_products * num_words
54
 
55
  def checkAllParticipantsEnded(self):
56
- self.defineSession()
57
-
58
- technique = self.session.tecnica
59
 
60
  expected_ratings_repetition = self.getExpectedRatings()
61
 
@@ -75,8 +86,8 @@ class MonitorSesionController():
75
  return (True, "Puedes finalizar la sesión")
76
 
77
  def finishSession(self):
78
- response = SesionController.finishRepetion(self.session)
79
  if isinstance(response, dict):
80
  return controller_error(response["error"])
81
- self.defineSession()
82
- return self.session
 
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, EsAtributo, EsVocabulario, Producto, Calificacion
5
  from tecnicas.controllers import ParticipacionController, SesionController
6
  from tecnicas.utils import controller_error
7
+ from .monitor_controller import MonitorController
8
 
9
 
10
+ class MonitorEscalasController(MonitorController):
11
+ url_view = "tecnicas/manage_sesions/monitor-sesion.html"
 
12
 
13
+ def __init__(self, session: SesionController):
14
+ super().__init__(session)
 
15
 
16
+ def controlGetResponse(self, request: HttpRequest, error: str = "", message: str = ""):
17
+ self.setContext()
 
 
 
 
 
 
18
 
19
+ if error != "" or error:
20
+ self.context["error"] = error
21
+ if message != "" or message:
22
+ self.context["message"] = message
23
 
24
+ return render(request, self.url_view, self.context)
25
+
26
+ def controlPostResponseFinishSession(self, request: HttpRequest):
27
+ self.setContext()
28
+ (is_all_end, message) = self.checkAllParticipantsEnded()
29
+ if not is_all_end:
30
+ self.context["error"] = message
31
+ return render(request, "tecnicas/manage_sesions/monitor-sesion.html", self.context)
32
+ response = self.finishSession()
33
+ if isinstance(response, dict):
34
+ self.context["error"] = response["error"]
35
+ return render(request, "tecnicas/manage_sesions/monitor-sesion.html", self.context)
36
+ self.context["message"] = message
37
+ return redirect(reverse("cata_system:detalles_sesion", kwargs={"session_code": self.sensorial_session.codigo_sesion}))
38
+
39
+ def setContext(self):
40
+ self.participations = ParticipacionController.getParticipationsInTechinique(
41
+ self.sensorial_session.tecnica)
42
+
43
+ self.context = {
44
+ "code_session": self.sensorial_session.codigo_sesion,
45
  "session_name": self.sensorial_session.nombre_sesion,
46
  "max_testers": self.sensorial_session.tecnica.limite_catadores,
47
  "current_testers": len(self.participations),
48
  "active_testers": len([part for part in self.participations if part.activo]),
49
+ "participations": self.participations,
50
+ "use_technique": self.sensorial_session.tecnica.tipo_tecnica.nombre_tecnica
51
  }
52
 
 
 
53
  def getExpectedRatings(self):
54
  num_products = Producto.objects.filter(
55
+ id_tecnica=self.sensorial_session.tecnica).count()
56
+ style_words = self.sensorial_session.tecnica.id_estilo
 
 
57
  num_words: int
58
 
59
  if style_words.nombre_estilo == "atributos":
60
  num_words = EsAtributo.objects.get(
61
+ id_tecnica=self.sensorial_session.tecnica).palabras.count()
62
  elif style_words.nombre_estilo == "vocabulario":
63
  num_words = EsVocabulario.objects.get(
64
+ id_tecnica=self.sensorial_session.tecnica).id_vocabulario.palabras.count()
65
 
66
  return num_products * num_words
67
 
68
  def checkAllParticipantsEnded(self):
69
+ technique = self.sensorial_session.tecnica
 
 
70
 
71
  expected_ratings_repetition = self.getExpectedRatings()
72
 
 
86
  return (True, "Puedes finalizar la sesión")
87
 
88
  def finishSession(self):
89
+ response = SesionController.finishRepetion(self.sensorial_session)
90
  if isinstance(response, dict):
91
  return controller_error(response["error"])
92
+ self.updataSession()
93
+ return self.sensorial_session
tecnicas/controllers/views_controller/tester_list_controller.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
2
+ from tecnicas.models import Catador
3
+ from tecnicas.utils import controller_error
4
+
5
+
6
+ class TesterListController():
7
+ def __init__(self, page: int):
8
+ self.num_page = page
9
+
10
+ def getContext(self):
11
+ view_context = {"num_page": self.num_page}
12
+
13
+ res_tuple = TesterListController.getTestersByPage(page=self.num_page)
14
+
15
+ if isinstance(res_tuple, dict):
16
+ view_context["error"] = res_tuple["error"]
17
+ return view_context
18
+
19
+ (testers, last_page) = res_tuple
20
+
21
+ view_context["testers"] = testers
22
+ view_context["last_page"] = last_page
23
+
24
+ return view_context
25
+
26
+ @staticmethod
27
+ def getTestersByPage(page: int):
28
+ elements_by_page = 6
29
+
30
+ queryset = (
31
+ Catador.objects
32
+ .select_related(
33
+ "user"
34
+ )
35
+ .only(
36
+ "user__first_name",
37
+ "user__last_name",
38
+ "user__username",
39
+ "user__email",
40
+ "genero",
41
+ "telefono",
42
+ "nacimiento"
43
+ )
44
+ .order_by("-user__date_joined")
45
+ )
46
+
47
+ paginator = Paginator(queryset, elements_by_page)
48
+ try:
49
+ testers_in_page = paginator.page(page)
50
+ except PageNotAnInteger:
51
+ return controller_error("índice inválido")
52
+
53
+ if not testers_in_page.object_list:
54
+ return controller_error("Sin registros de Catadores")
55
+
56
+ is_last_page = not testers_in_page.number < paginator.num_pages
57
+
58
+ return (testers_in_page, is_last_page)
tecnicas/decorators/required_presenter.py CHANGED
@@ -8,7 +8,7 @@ def required_presenter(view_func):
8
  if not request.user.is_authenticated:
9
  return redirect("cata_system:autenticacion")
10
 
11
- if not hasattr(request.user, "presentador"):
12
  raise PermissionDenied(
13
  "Solo los presentadores pueden acceder a esta vista")
14
  return view_func(request, *args, **kwargs)
 
8
  if not request.user.is_authenticated:
9
  return redirect("cata_system:autenticacion")
10
 
11
+ if not hasattr(request.user, "user_presentador"):
12
  raise PermissionDenied(
13
  "Solo los presentadores pueden acceder a esta vista")
14
  return view_func(request, *args, **kwargs)
tecnicas/forms/catador_form.py CHANGED
@@ -1,25 +1,148 @@
1
  from django import forms
2
- from ..models import Catador
3
-
4
- class CatadorForm(forms.ModelForm):
5
- class Meta:
6
- model = Catador
7
- fields = "__all__"
8
- widgets = {
9
- "usuarioCatador": forms.TextInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
10
- "nombre": forms.TextInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
11
- "apellido": forms.TextInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
12
- "telefono": forms.NumberInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general", "max": "10", "min": "10" } ),
13
- "correo": forms.EmailInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
14
- "fechaNacimiento": forms.DateInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general", "type": "date", } ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  def clean(self):
18
- data_cleand = super().clean()
19
-
20
- phone = data_cleand["telefono"]
21
- size_phone = len(str(abs(phone)))
22
- print("tamano de telefono", size_phone)
23
- if size_phone != 10:
24
- self.add_error("telefono", "telefono debe tener 10 digitos")
25
- return data_cleand
 
 
 
 
 
1
  from django import forms
2
+ from django.contrib.auth.models import User
3
+ from django.core.validators import RegexValidator, EmailValidator
4
+
5
+
6
+ class CatadorForm(forms.Form):
7
+ styles_input = "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-alter-card"
8
+
9
+ nombre_usuario = forms.CharField(
10
+ label="Nombre de usuario",
11
+ max_length=30,
12
+ min_length=5,
13
+ required=True,
14
+ validators=[
15
+ RegexValidator(
16
+ regex=r'^(?![0-9._])[A-Za-z0-9._]+$',
17
+ message=(
18
+ "El nombre de usuario debe iniciar con una letra y solo puede "
19
+ "contener letras, números, puntos y guiones bajos."
20
+ ),
21
+ code='invalid_username'
22
+ )
23
+ ],
24
+ widget=forms.TextInput(
25
+ attrs={
26
+ "placeholder": "Ej. mario_hugo23",
27
+ "class": styles_input,
28
+ }
29
+ )
30
+ )
31
+
32
+ nombre = forms.CharField(
33
+ label="Nombre del catador",
34
+ max_length=50,
35
+ required=True,
36
+ validators=[
37
+ RegexValidator(
38
+ regex=r'^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$',
39
+ message="El nombre solo puede contener letras y espacios."
40
+ )
41
+ ],
42
+ widget=forms.TextInput(
43
+ attrs={
44
+ "placeholder": "Ej. Mario",
45
+ "class": styles_input,
46
+ }
47
+ )
48
+ )
49
+
50
+ apellido = forms.CharField(
51
+ label="Apellido del catador",
52
+ max_length=50,
53
+ required=True,
54
+ validators=[
55
+ RegexValidator(
56
+ regex=r'^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$',
57
+ message="El apellido solo puede contener letras y espacios."
58
+ )
59
+ ],
60
+ widget=forms.TextInput(
61
+ attrs={
62
+ "placeholder": "Ej. Sánchez",
63
+ "class": styles_input,
64
+ }
65
+ )
66
+ )
67
+
68
+ telefono = forms.CharField(
69
+ label="Teléfono",
70
+ max_length=10,
71
+ required=True,
72
+ validators=[
73
+ RegexValidator(
74
+ regex=r'^\d{10}$',
75
+ message="El teléfono debe contener solo números (puede incluir + al inicio)."
76
+ )
77
+ ],
78
+ widget=forms.TextInput(
79
+ attrs={
80
+ "placeholder": "Ej. 5512345678",
81
+ "class": styles_input,
82
+ "type": "tel",
83
+ }
84
+ )
85
+ )
86
+
87
+ correo = forms.EmailField(
88
+ label="Correo electrónico",
89
+ required=True,
90
+ validators=[EmailValidator(
91
+ message="Introduce un correo electrónico válido.")],
92
+ widget=forms.EmailInput(
93
+ attrs={
94
+ "placeholder": "Ej. mario@example.com",
95
+ "class": styles_input,
96
+ }
97
+ )
98
+ )
99
+
100
+ fecha_nacimiento = forms.DateField(
101
+ label="Fecha de nacimiento",
102
+ required=True,
103
+ widget=forms.DateInput(
104
+ attrs={
105
+ "type": "date",
106
+ "class": styles_input,
107
+ }
108
+ ),
109
+ error_messages={
110
+ "invalid": "Introduce una fecha válida (DD-MM-YYYY)."
111
  }
112
+ )
113
+
114
+ GENERO_OPCIONES = [
115
+ ('Hombre', 'Hombre'),
116
+ ('Mujer', 'Mujer')
117
+ ]
118
+
119
+ genero = forms.ChoiceField(
120
+ label="Género",
121
+ required=True,
122
+ choices=GENERO_OPCIONES,
123
+ widget=forms.RadioSelect(
124
+ attrs={
125
+ "class": "ct-inputs-pos-cata radio radio-warning checked:bg-btn-secondary mx-2 bg-surface-ligt disabled:bg-ct-error"
126
+ }
127
+ )
128
+ )
129
+
130
+ is_update = forms.BooleanField(
131
+ initial=False,
132
+ required=False,
133
+ widget=forms.HiddenInput()
134
+ )
135
 
136
  def clean(self):
137
+ cleaned_data = super().clean()
138
+ username = cleaned_data.get("nombre_usuario")
139
+ is_update = cleaned_data.get("is_update")
140
+
141
+ print(username, is_update)
142
+
143
+ if not is_update:
144
+ if User.objects.filter(username__iexact=username).exists():
145
+ raise forms.ValidationError(
146
+ "Este nombre de usuario ya está registrado.")
147
+
148
+ return cleaned_data
tecnicas/forms/codes_form.py CHANGED
@@ -1,10 +1,11 @@
1
  from django import forms
2
 
 
3
  class CodesForm(forms.Form):
4
- def __init__(self, *args, codes = [], **kwargs):
5
  super().__init__(*args, **kwargs)
6
 
7
  for index, code in enumerate(codes):
8
  self.fields[f'producto_{index+1}'] = forms.CharField(max_length=3, required=True, min_length=3, initial=code, label=f"codigo {index+1}", widget=forms.TextInput(attrs={
9
  "class": "ct-code bg-surface-ligt p-1 border-b-1 text-center w-full disabled:bg-surface-general uppercase"
10
- }))
 
1
  from django import forms
2
 
3
+
4
  class CodesForm(forms.Form):
5
+ def __init__(self, *args, codes=[], **kwargs):
6
  super().__init__(*args, **kwargs)
7
 
8
  for index, code in enumerate(codes):
9
  self.fields[f'producto_{index+1}'] = forms.CharField(max_length=3, required=True, min_length=3, initial=code, label=f"codigo {index+1}", widget=forms.TextInput(attrs={
10
  "class": "ct-code bg-surface-ligt p-1 border-b-1 text-center w-full disabled:bg-surface-general uppercase"
11
+ }))
tecnicas/forms/sesion_basic_form.py CHANGED
@@ -5,7 +5,8 @@ from ..models import EstiloPalabra
5
 
6
 
7
  class SesionBasicForm(forms.Form):
8
- id_tecnica = forms.IntegerField(widget=forms.HiddenInput())
 
9
 
10
  nombre_sesion = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
11
  "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
@@ -28,36 +29,37 @@ class SesionBasicForm(forms.Form):
28
  "placeholder": "Solo números"
29
  }), required=True)
30
 
31
- tamano_escala = forms.IntegerField(widget=forms.NumberInput(attrs={
32
- "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
33
- }), required=True, min_value=5)
34
-
35
  instrucciones = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
36
  "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
37
  "placeholder": "Este campo es opcional"
38
  }), required=False)
39
 
40
- def __init__(self, *args, id_tecnica_new=0, **kwargs):
41
  super().__init__(*args, **kwargs)
42
 
 
 
 
43
  self.fields['estilo_palabras'] = forms.ModelChoiceField(queryset=EstiloPalabra.objects.all(), widget=forms.RadioSelect(attrs={
44
- "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
45
  }), required=True, initial=EstiloPalabra.objects.first())
46
 
47
  self.fields['tipo_escala'] = forms.ModelChoiceField(queryset=TipoEscala.objects.all(), widget=forms.RadioSelect(attrs={
48
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
49
  }), required=True, initial=TipoEscala.objects.first())
50
 
51
- if id_tecnica_new != 0:
52
- self.fields['id_tecnica'] = forms.IntegerField(
53
- initial=id_tecnica_new, widget=forms.HiddenInput())
54
 
55
- def clean(self):
56
- data_clean = super().clean()
57
 
58
- sizes_estruturada = [5, 7, 9]
59
- sizes_continua = [9, 12, 15]
60
 
 
 
61
  escala = data_clean.get("tipo_escala")
62
 
63
  if escala and not isinstance(escala, TipoEscala):
@@ -70,14 +72,7 @@ class SesionBasicForm(forms.Form):
70
 
71
  tamano_escala = data_clean.get("tamano_escala")
72
 
73
- if escala.nombre_escala == "estructurada" and not sizes_estruturada.__contains__(tamano_escala):
74
  self.add_error("tamano_escala", "El tamaño de la escala no aplica")
75
- elif escala.nombre_escala == "continua" and not sizes_continua.__contains__(tamano_escala):
76
  self.add_error("tamano_escala", "El tamaño de la escala no aplica")
77
-
78
- id_tecnica = data_clean.get("id_tecnica")
79
-
80
- try:
81
- tecnica = TipoTecnica.objects.get(pk=id_tecnica)
82
- except (ValueError, TipoTecnica.DoesNotExist):
83
- return data_clean
 
5
 
6
 
7
  class SesionBasicForm(forms.Form):
8
+ sizes_structure = [5, 7, 9]
9
+ sizes_continue = [9, 13, 15]
10
 
11
  nombre_sesion = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
12
  "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
 
29
  "placeholder": "Solo números"
30
  }), required=True)
31
 
 
 
 
 
32
  instrucciones = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
33
  "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
34
  "placeholder": "Este campo es opcional"
35
  }), required=False)
36
 
37
+ def __init__(self, *args, initial_conf: dict = None, **kwargs):
38
  super().__init__(*args, **kwargs)
39
 
40
+ if initial_conf is None:
41
+ initial_conf = {}
42
+
43
  self.fields['estilo_palabras'] = forms.ModelChoiceField(queryset=EstiloPalabra.objects.all(), widget=forms.RadioSelect(attrs={
44
+ "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
45
  }), required=True, initial=EstiloPalabra.objects.first())
46
 
47
  self.fields['tipo_escala'] = forms.ModelChoiceField(queryset=TipoEscala.objects.all(), widget=forms.RadioSelect(attrs={
48
  "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
49
  }), required=True, initial=TipoEscala.objects.first())
50
 
51
+ self.fields['tamano_escala'] = forms.IntegerField(widget=forms.HiddenInput(attrs={
52
+ "class": "cts-size-input",
53
+ }), required=True)
54
 
55
+ if "numero_catadores" in initial_conf:
56
+ self.fields["numero_catadores"].initial = initial_conf["numero_catadores"]
57
 
58
+ if "numero_repeticiones" in initial_conf:
59
+ self.fields["numero_repeticiones"].initial = initial_conf["numero_repeticiones"]
60
 
61
+ def clean(self):
62
+ data_clean = super().clean()
63
  escala = data_clean.get("tipo_escala")
64
 
65
  if escala and not isinstance(escala, TipoEscala):
 
72
 
73
  tamano_escala = data_clean.get("tamano_escala")
74
 
75
+ if escala.nombre_escala == "estructurada" and not self.sizes_structure.__contains__(tamano_escala):
76
  self.add_error("tamano_escala", "El tamaño de la escala no aplica")
77
+ elif escala.nombre_escala == "continua" and not self.sizes_continue.__contains__(tamano_escala):
78
  self.add_error("tamano_escala", "El tamaño de la escala no aplica")
 
 
 
 
 
 
 
tecnicas/forms/sesion_tags_form.py CHANGED
@@ -1,25 +1,35 @@
1
  from django import forms
2
-
3
  from ..models import Etiqueta
4
 
 
5
  class SesionTagsForm(forms.Form):
6
- def __init__(self, *args, longitud=None, tipo_escala:str=None, **kwargs):
 
 
 
 
 
 
 
 
 
7
  super().__init__(*args, **kwargs)
8
 
9
  if tipo_escala == "estructurada":
 
10
  for i in range(longitud):
11
- self.fields[f'segmento_{i+1}'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label=f"segmento {i+1}", empty_label="Selecione opcion", widget=forms.Select(attrs={
12
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
13
  }))
14
  else:
15
- self.fields['punto_inicial'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label="Punto inicial", empty_label="Selecione opcion", widget=forms.Select(attrs={
16
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
17
- }))
18
-
19
- self.fields['punto_medio'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label="Punto medio", empty_label="Selecione opcion", widget=forms.Select(attrs={
20
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
21
- }))
22
-
23
- self.fields['punto_final'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label="Punto final", empty_label="Selecione opcion", widget=forms.Select(attrs={
24
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
25
- }))
 
1
  from django import forms
 
2
  from ..models import Etiqueta
3
 
4
+
5
  class SesionTagsForm(forms.Form):
6
+ available_struture_tags = {
7
+ "5": ["percepcion nula", "percepcion moderada", "se puede percibir",
8
+ "percepcion intensa", "percepcion total"],
9
+ "7": ["percepcion nula", "ligera percepcion", "percepcion moderada", "se puede percibir", "buena percepcion",
10
+ "percepcion intensa", "percepcion total"],
11
+ "9": ["percepcion nula", "ligera percepcion", "poca percepcion", "percepcion moderada", "se puede percibir", "buena percepcion",
12
+ "percepcion intensa", "percepcion muy intensa", "percepcion total"]
13
+ }
14
+
15
+ def __init__(self, *args, longitud=None, tipo_escala: str = None, **kwargs):
16
  super().__init__(*args, **kwargs)
17
 
18
  if tipo_escala == "estructurada":
19
+ use_tags = self.available_struture_tags[f"{longitud}"]
20
  for i in range(longitud):
21
+ self.fields[f'segmento_{i+1}'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta=use_tags[i]), required=True, label=f"segmento {i+1}", empty_label="Selecione opcion", widget=forms.Select(attrs={
22
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
23
  }))
24
  else:
25
+ self.fields['punto_inicial'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta="percepcion nula"), required=True, label="Punto inicial", empty_label="Selecione opcion", widget=forms.Select(attrs={
26
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
27
+ }))
28
+
29
+ self.fields['punto_medio'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta="se puede percibir"), required=True, label="Punto medio", empty_label="Selecione opcion", widget=forms.Select(attrs={
30
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
31
+ }))
32
+
33
+ self.fields['punto_final'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta="percepcion total"), required=True, label="Punto final", empty_label="Selecione opcion", widget=forms.Select(attrs={
34
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
35
+ }))
tecnicas/middlewares/presenter_middleware.py CHANGED
@@ -1,18 +1,17 @@
1
- from django.core.exceptions import PermissionDenied
2
-
3
  class PresenterAccessMiddleware:
4
  def __init__(self, get_response):
5
  self.get_response = get_response
6
 
7
  def __call__(self, request):
8
  path = request.path_info
9
-
10
  if path.startswith('/cata/presenter/'):
11
  if not request.user.is_authenticated:
12
  from django.shortcuts import redirect
13
  return redirect("cata_system:autenticacion")
14
 
15
  if not hasattr(request.user, 'user_presentador'):
 
16
  return redirect("cata_system:autenticacion")
17
 
18
  return self.get_response(request)
 
 
 
1
  class PresenterAccessMiddleware:
2
  def __init__(self, get_response):
3
  self.get_response = get_response
4
 
5
  def __call__(self, request):
6
  path = request.path_info
7
+
8
  if path.startswith('/cata/presenter/'):
9
  if not request.user.is_authenticated:
10
  from django.shortcuts import redirect
11
  return redirect("cata_system:autenticacion")
12
 
13
  if not hasattr(request.user, 'user_presentador'):
14
+ from django.shortcuts import redirect
15
  return redirect("cata_system:autenticacion")
16
 
17
  return self.get_response(request)
tecnicas/middlewares/tester_middleware.py CHANGED
@@ -9,38 +9,13 @@ class TesterAccessMiddleware:
9
  def __call__(self, request):
10
  path = request.path_info
11
 
12
- if path.startswith('/cata/tester/'):
13
  if not request.user.is_authenticated:
14
  from django.shortcuts import redirect
15
  return redirect("cata_system:catador_login")
16
 
17
- if not hasattr(request.user, 'catador'):
 
18
  return redirect("cata_system:catador_login")
19
 
20
  return self.get_response(request)
21
-
22
- # from django.http import HttpRequest
23
-
24
-
25
- # class LoginTesterMiddleware():
26
- # def __init__(self, get_response):
27
- # self.get_response = get_response
28
-
29
- # def __call__(self, req: HttpRequest):
30
- # base_url_protected = "/cata/testers/"
31
-
32
- # if req.path.startswith(base_url_protected):
33
- # if not "cata_username" in req.session:
34
- # id_participacion = req.COOKIES.get("id_participacion")
35
- # if id_participacion:
36
- # from tecnicas.controllers import ParticipacionController
37
- # ParticipacionController.outSession(id_participacion)
38
- # from django.shortcuts import redirect
39
- # from django.urls import reverse
40
- # response = redirect(reverse("cata_system:catador_login"))
41
- # response.delete_cookie("id_participacion")
42
- # return response
43
-
44
- # response = self.get_response(req)
45
-
46
- # return response
 
9
  def __call__(self, request):
10
  path = request.path_info
11
 
12
+ if path.startswith('/cata/testers/'):
13
  if not request.user.is_authenticated:
14
  from django.shortcuts import redirect
15
  return redirect("cata_system:catador_login")
16
 
17
+ if not hasattr(request.user, 'user_catador'):
18
+ from django.shortcuts import redirect
19
  return redirect("cata_system:catador_login")
20
 
21
  return self.get_response(request)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/models/participacion.py CHANGED
@@ -12,4 +12,4 @@ class Participacion(models.Model):
12
  finalizado = models.BooleanField(default=True)
13
 
14
  def __str__(self):
15
- return f"{self.catador.usuarioCatador} {'activo' if self.activo else 'no activo'}"
 
12
  finalizado = models.BooleanField(default=True)
13
 
14
  def __str__(self):
15
+ return f"{self.catador.user.username} {'activo' if self.activo else 'no activo'}"
tecnicas/models/sesion_sensorial.py CHANGED
@@ -17,4 +17,9 @@ class SesionSensorial(models.Model):
17
  Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
18
 
19
  def __str__(self):
20
- return self.nombre_sesion if self.nombre_sesion else self.codigo_sesion
 
 
 
 
 
 
17
  Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
18
 
19
  def __str__(self):
20
+ name_str = f"{self.codigo_sesion}"
21
+
22
+ if self.nombre_sesion:
23
+ name_str += f" : {self.nombre_sesion}"
24
+
25
+ return name_str
tecnicas/static/js/catador-craete.js CHANGED
@@ -7,7 +7,6 @@ for (let index = 0; index < inputs.length; index++) {
7
 
8
  function hiddenErrorAndMessages(e) {
9
  for (let index = 0; index < notifiactions.length; index++) {
10
- console.log("Oculto");
11
  notifiactions.item(index).classList.add("hidden");
12
  }
13
  }
 
7
 
8
  function hiddenErrorAndMessages(e) {
9
  for (let index = 0; index < notifiactions.length; index++) {
 
10
  notifiactions.item(index).classList.add("hidden");
11
  }
12
  }
tecnicas/static/js/create-session.js CHANGED
@@ -90,7 +90,7 @@ function renderElementsResponse({
90
  ];
91
 
92
  const aIndex = document.createElement("a");
93
- aIndex.href = "/cata";
94
  aIndex.textContent = "Volver al inicio";
95
 
96
  aIndex.classList.add(
@@ -99,7 +99,7 @@ function renderElementsResponse({
99
  );
100
 
101
  const aDetails = document.createElement("a");
102
- aDetails.href = `/cata/detalles-sesion/${sessionId}`;
103
  aDetails.textContent = "Ver detalles la sesion";
104
 
105
  aDetails.classList.add(
 
90
  ];
91
 
92
  const aIndex = document.createElement("a");
93
+ aIndex.href = "/cata/presenter/";
94
  aIndex.textContent = "Volver al inicio";
95
 
96
  aIndex.classList.add(
 
99
  );
100
 
101
  const aDetails = document.createElement("a");
102
+ aDetails.href = `/cata/presenter/detalles-sesion/${sessionId}`;
103
  aDetails.textContent = "Ver detalles la sesion";
104
 
105
  aDetails.classList.add(
tecnicas/static/js/details-session.js CHANGED
@@ -4,23 +4,17 @@ const notificationError = document.querySelector(".ct-notification-error");
4
  if (notificationError) {
5
  setTimeout(function () {
6
  notificationError.classList.add("hidden");
7
- }, 2000);
8
  }
9
 
10
  function startRepetition() {
11
- const inputAction = document.createElement("input");
12
- inputAction.type = "hidden";
13
- inputAction.name = "action";
14
- inputAction.value = "start_session";
15
-
16
- const inputUser = document.createElement("input");
17
- inputUser.type = "hidden";
18
- inputUser.name = "username";
19
- inputUser.value = "aguBido";
20
-
21
- actionForm.appendChild(inputAction);
22
- actionForm.appendChild(inputUser);
23
-
24
- actionForm.classList.remove("hidden");
25
  actionForm.submit();
26
  }
 
 
 
 
 
 
 
4
  if (notificationError) {
5
  setTimeout(function () {
6
  notificationError.classList.add("hidden");
7
+ }, 3000);
8
  }
9
 
10
  function startRepetition() {
11
+ const input = actionForm.querySelector(".action-option")
12
+ input.value = "start_session";
 
 
 
 
 
 
 
 
 
 
 
 
13
  actionForm.submit();
14
  }
15
+
16
+ function deleteSession() {
17
+ const input = actionForm.querySelector(".action-option")
18
+ input.value = "delete_session";
19
+ actionForm.submit();
20
+ }
tecnicas/static/js/panel-basic.js CHANGED
@@ -1,6 +1,6 @@
1
  const descriptons = {
2
- estructurada: ["Establece número de segmentos:", "Puede ser 5, 7 o 9"],
3
- continua: ["Establece la longitud de la escala:", "Puede ser 9, 12 o 15"],
4
  atributos:
5
  "Con el estilo atributos elijes las palabras para evaluar los productos",
6
  vocabulario:
@@ -12,6 +12,11 @@ let inputTamano;
12
 
13
  let inputsStyle;
14
  let helpStyle;
 
 
 
 
 
15
 
16
  initPanel();
17
 
@@ -22,6 +27,7 @@ function initPanel() {
22
  function initRadios() {
23
  inputsScale = document.getElementsByName("tipo_escala");
24
  inputTamano = document.getElementsByName("tamano_escala").item(0);
 
25
 
26
  for (let index = 0; index < inputsScale.length; index++) {
27
  let parent = inputsScale.item(index).parentElement;
@@ -46,6 +52,73 @@ function initRadios() {
46
  showDescriptionStyle(parent);
47
  }
48
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
 
51
  function showDescriptionStyle(label) {
@@ -56,6 +129,5 @@ function showDescriptionStyle(label) {
56
  function showDescriptionTamanoScale(label) {
57
  const text = label.textContent.trim();
58
  let parent = inputTamano.parentElement;
59
- parent.getElementsByTagName("p")[0].textContent = descriptons[text][0];
60
- inputTamano.placeholder = descriptons[text][1];
61
  }
 
1
  const descriptons = {
2
+ estructurada: "Establece número de segmentos:",
3
+ continua: "Establece la longitud de la escala:",
4
  atributos:
5
  "Con el estilo atributos elijes las palabras para evaluar los productos",
6
  vocabulario:
 
12
 
13
  let inputsStyle;
14
  let helpStyle;
15
+ let sizeOptionsContainer;
16
+ const SIZE_OPTIONS = {
17
+ estructurada: [5, 7, 9],
18
+ continua: [9, 13, 15],
19
+ };
20
 
21
  initPanel();
22
 
 
27
  function initRadios() {
28
  inputsScale = document.getElementsByName("tipo_escala");
29
  inputTamano = document.getElementsByName("tamano_escala").item(0);
30
+ sizeOptionsContainer = document.getElementsByClassName("cts-options-size-scale")[0];
31
 
32
  for (let index = 0; index < inputsScale.length; index++) {
33
  let parent = inputsScale.item(index).parentElement;
 
52
  showDescriptionStyle(parent);
53
  }
54
  }
55
+
56
+ initSizeOptions();
57
+ }
58
+
59
+ function initSizeOptions() {
60
+ for (let i = 0; i < inputsScale.length; i++) {
61
+ const radio = inputsScale.item(i);
62
+ radio.addEventListener('change', () => {
63
+ const tag = getTagFromLabel(radio);
64
+ populateSizeOptions(tag);
65
+ });
66
+ if (radio.checked) {
67
+ const tag = getTagFromLabel(radio);
68
+ populateSizeOptions(tag);
69
+ }
70
+ }
71
+
72
+ const form = document.querySelector('form');
73
+ if (!form) return;
74
+
75
+ form.addEventListener('submit', (e) => {
76
+ const chosen = document.querySelector('input[name="option_size_scale"]:checked');
77
+ if (chosen && inputTamano) {
78
+ inputTamano.value = chosen.value;
79
+ }
80
+
81
+ if (sizeOptionsContainer) {
82
+ const toRemove = sizeOptionsContainer.querySelectorAll('input[name="option_size_scale"]');
83
+ toRemove.forEach((el) => el.remove());
84
+ }
85
+ });
86
+ }
87
+
88
+ function getTagFromLabel(radio) {
89
+ try {
90
+ const parent = radio.parentElement;
91
+ if (!parent) return '';
92
+ const text = parent.textContent || '';
93
+ return text.trim().split(/\s+/)[0].toLowerCase();
94
+ } catch (err) {
95
+ return '';
96
+ }
97
+ }
98
+
99
+ function populateSizeOptions(tag) {
100
+ const options = SIZE_OPTIONS[tag] || SIZE_OPTIONS['estructurada'];
101
+ if (!sizeOptionsContainer) return;
102
+ sizeOptionsContainer.innerHTML = '';
103
+
104
+ options.forEach((val) => {
105
+ const label = document.createElement('label');
106
+ label.className = 'flex flex-col items-center cursor-pointer';
107
+
108
+ const input = document.createElement('input');
109
+ input.type = 'radio';
110
+ input.name = 'option_size_scale';
111
+ input.value = String(val);
112
+ input.className = 'radio radio-lg checked:bg-pink-500';
113
+
114
+ const span = document.createElement('span');
115
+ span.className = 'mt-2 text-xl text-gray-700 font-medium';
116
+ span.textContent = String(val);
117
+
118
+ label.appendChild(input);
119
+ label.appendChild(span);
120
+ sizeOptionsContainer.appendChild(label);
121
+ });
122
  }
123
 
124
  function showDescriptionStyle(label) {
 
129
  function showDescriptionTamanoScale(label) {
130
  const text = label.textContent.trim();
131
  let parent = inputTamano.parentElement;
132
+ parent.getElementsByTagName("p")[0].textContent = descriptons[text];
 
133
  }
tecnicas/static/js/panel-words.js CHANGED
@@ -28,7 +28,7 @@ async function getWordsByName(e) {
28
  palabra: dataForm.get("search").trim(),
29
  });
30
 
31
- const url = `api/palabras?${params}`;
32
 
33
  try {
34
  const respone = await fetch(url, {
 
28
  palabra: dataForm.get("search").trim(),
29
  });
30
 
31
+ const url = `/cata/api/palabras?${params}`;
32
 
33
  try {
34
  const respone = await fetch(url, {
tecnicas/static/js/showHiddenElement.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ function hiddenWarningDialog(styleClass) {
2
+ const element = document.querySelector(`.${styleClass}`)
3
+ element.classList.add("hidden")
4
+ }
5
+
6
+ function showWarningDialog(styleClass) {
7
+ const element = document.querySelector(`.${styleClass}`)
8
+ element.classList.remove("hidden")
9
+ }
tecnicas/templates/tecnicas/auth.html CHANGED
@@ -26,7 +26,7 @@
26
  </label>
27
 
28
  <label for="password">
29
- <input type="text" name="password" id="password" placeholder="Ingrese Contraseña"
30
  class="placeholder:text-gray-400 placeholder:font-semibold text-xl max-sm:text-base bg-surface-ligt py-3 px-6 rounded-xl lg:w-sm border-b-2 border-surface-sweet"
31
  required>
32
  </label>
 
26
  </label>
27
 
28
  <label for="password">
29
+ <input type="password" name="password" id="password" placeholder="Ingrese Contraseña"
30
  class="placeholder:text-gray-400 placeholder:font-semibold text-xl max-sm:text-base bg-surface-ligt py-3 px-6 rounded-xl lg:w-sm border-b-2 border-surface-sweet"
31
  required>
32
  </label>
tecnicas/templates/tecnicas/cata-login.html CHANGED
@@ -13,23 +13,15 @@
13
  </header>
14
 
15
  {% if error %}
16
- <article class="bg-ct-error p-4 text-white rounded-xl ct-notification-error">
17
- <p
18
- class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
19
- {{ error }}
20
- </p>
21
- </article>
22
  {% endif %}
23
 
24
  <article class="flex flex-col gap-6 items-center w-full mt-5">
25
- <label for="id">
26
- <input type="text" name="code_session" id="id" placeholder="Codigo de sesion"
27
- class="placeholder:text-gray-400 placeholder:font-semibold text-xl max-sm:text-base bg-surface-ligt py-3 px-6 rounded-xl lg:w-sm border-b-2 border-surface-sweet"
28
- required>
29
- </label>
30
-
31
- <label for="id">
32
- <input type="text" name="user_tester" id="id" placeholder="Nombre de usuario"
33
  class="placeholder:text-gray-400 placeholder:font-semibold text-xl max-sm:text-base bg-surface-ligt py-3 px-6 rounded-xl lg:w-sm border-b-2 border-surface-sweet"
34
  required>
35
  </label>
@@ -44,15 +36,4 @@
44
  </form>
45
  </article>
46
  </article>
47
- {% endblock %}
48
-
49
- {% block extra_js %}
50
- <script>
51
- const error = document.querySelector(".ct-notification-error")
52
- if (error) {
53
- setTimeout(() => {
54
- error.classList.add("hidden")
55
- }, 4000)
56
- }
57
- </script>
58
  {% endblock %}
 
13
  </header>
14
 
15
  {% if error %}
16
+ {% include "./components/error-message.html" with message=error %}
17
+ {% endif %}
18
+ {% if message %}
19
+ {% include "./components/error-message.html" with message=message %}
 
 
20
  {% endif %}
21
 
22
  <article class="flex flex-col gap-6 items-center w-full mt-5">
23
+ <label for="tester">
24
+ <input type="text" name="user_tester" id="tester" placeholder="Nombre de usuario"
 
 
 
 
 
 
25
  class="placeholder:text-gray-400 placeholder:font-semibold text-xl max-sm:text-base bg-surface-ligt py-3 px-6 rounded-xl lg:w-sm border-b-2 border-surface-sweet"
26
  required>
27
  </label>
 
36
  </form>
37
  </article>
38
  </article>
 
 
 
 
 
 
 
 
 
 
 
39
  {% endblock %}
tecnicas/templates/tecnicas/components/item-tester.html ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div
2
+ class="card w-full min-w-2xs max-w-sm bg-surface-ligt shadow-md hover:shadow-lg transition-shadow duration-300 text-black">
3
+ <div class="card-body">
4
+ <h2 class="text-center text-lg font-bold bg-surface-sweet rounded">
5
+ {{ tester.user.first_name }} {{ tester.user.last_name }}
6
+ </h2>
7
+ <p class="text-lg text-center bg-surface-alter-card rounded px-2">
8
+ <span class="font-bold">Usuario: </span>
9
+ {{ tester.user.username }}
10
+ </p>
11
+
12
+ <div class="w-full text-base flex gap-2 flex-wrap text-center">
13
+ <p class="font-semibold bg-btn-secondary rounded text-white p-1">
14
+ <span class="font-bold">Genero:</span>
15
+ {{ tester.genero }}
16
+ </p>
17
+ <p class="font-semibold bg-btn-secondary rounded text-white p-1">
18
+ <span class="font-bold">Teléfono: </span>
19
+ {{ tester.telefono }}
20
+ </p>
21
+ <p class="font-semibold bg-btn-secondary rounded text-white p-1">
22
+ <span class="font-bold">Email: </span>
23
+ {{ tester.user.email }}
24
+ </p>
25
+ <p class="font-semibold bg-btn-secondary rounded text-white p-1">
26
+ <span class="font-bold">Nacimiento: </span>
27
+ {{ tester.nacimiento|date:"d/m/Y" }}
28
+ </p>
29
+ </div>
30
+
31
+ <div class="card-actions justify-end mt-3">
32
+ <a href="{% url 'cata_system:buscar_catador' %}?user={{ tester.user.username }}" class="w-full">
33
+ <button class="btn bg-btn-primary border-0 block w-full btn-push">
34
+ Actualizar datos
35
+ </button>
36
+ </a>
37
+ </div>
38
+ </div>
39
+ </div>
tecnicas/templates/tecnicas/components/item_session.html ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div
2
+ class='card {{ sesion.activo|yesno:"bg-surface-ligt,bg-surface-card" }} shadow-lg text-black sm:max-w-80 w-full flex justify-center items-center'>
3
+ <div class="card-body flex flex-col justify-between w-full">
4
+ <div class="flex flex-col gap-1 [&>*]:first:text-xl [&>*]:first:font-bold [&>*]:first:border-b">
5
+ {% if sesion.nombre_sesion %}
6
+ <h2>{{ sesion.nombre_sesion }}</h2>
7
+ <span class="text-lg underline"><b>Código:</b><br>{{ sesion.codigo_sesion }}</span>
8
+ {% else %}
9
+ <span class="text-lg">Código:<br>{{ sesion.codigo_sesion }}</span>
10
+ {% endif %}
11
+ </div>
12
+ <ul class="mt-6 flex flex-col gap-2 text-base">
13
+ <li>
14
+ Se usa la Técnica:
15
+ <span class="uppercase font-bold">
16
+ {{ sesion.tecnica.tipo_tecnica.nombre_tecnica }}
17
+ </span>
18
+ </li>
19
+ <li>
20
+ El Estilo de palabras:
21
+ <span class="uppercase font-bold">
22
+ {{ sesion.tecnica.id_estilo.nombre_estilo }}
23
+ </span>
24
+ </li>
25
+ <li>
26
+ {{ sesion.fechaCreacion }}
27
+ </li>
28
+ <li>
29
+ Estado:
30
+ <span class="uppercase font-bold">
31
+ {% if sesion.activo %}
32
+ Activo
33
+ {% else %}
34
+ No activo
35
+ {% endif %}
36
+ </span>
37
+ </li>
38
+ </ul>
39
+ <div class="mt-6">
40
+ <a href="{% url 'cata_system:detalles_sesion' session_code=sesion.codigo_sesion %}">
41
+ <button class="btn bg-btn-primary border-0 block w-full btn-push">
42
+ Inspeccionar sesión
43
+ </button>
44
+ </a>
45
+ </div>
46
+ </div>
47
+ </div>
tecnicas/templates/tecnicas/components/item_session_tester.html ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="card bg-surface-sweet border border-base-300 shadow-md hover:shadow-lg transition">
2
+ <div class="card-body space-y-4">
3
+ <h4 class="card-title text-lg font-bold">
4
+ {% if session.nombre_sesion %}
5
+ {{ session.nombre_sesion }}
6
+ {% else %}
7
+ <span class="italic text-gray-500">Sin nombre</span>
8
+ {% endif %}
9
+ </h4>
10
+
11
+ <article class="text-sm space-y-1">
12
+ <p><span class="font-semibold">Código:</span> {{ session.codigo_sesion }}</p>
13
+ <p><span class="font-semibold">Técnica:</span> {{ session.tecnica.tipo_tecnica }}</p>
14
+ <p><span class="font-semibold">Fecha:</span> {{ session.fechaCreacion }}</p>
15
+ <p>
16
+ <span class="font-semibold">Finazliado:</span>
17
+ {% if session.participacion_finalizado %}
18
+ <span class="badge badge-success">Se ha concluido</span>
19
+ {% else %}
20
+ <span class="badge badge-warning">Sin concluir</span>
21
+ {% endif %}
22
+ </p>
23
+ <p>
24
+ <span class="font-semibold">Estatus:</span>
25
+ {% if session.activo %}
26
+ <span class="badge badge-success">En proceso</span>
27
+ {% else %}
28
+ <span class="badge badge-error">No activo</span>
29
+ {% endif %}
30
+ </p>
31
+ </article>
32
+ <article>
33
+ <a href="{% url 'cata_system:catador_init_session' code_sesion=session.codigo_sesion %}">
34
+ <button class="btn bg-btn-primary border-0 block w-full btn-push">
35
+ Inspeccionar
36
+ </button>
37
+ </a>
38
+ </article>
39
+ </div>
40
+ </div>
tecnicas/templates/tecnicas/components/table-convencional.html CHANGED
@@ -23,7 +23,6 @@
23
  {% with match=None %}
24
  {% for valor in valores %}
25
  {% if valor.nombre_palabra == palabra %}
26
- {{ palabra }} <br>
27
  {{ valor.dato_valor }}
28
  {% with match=True %}{% endwith %}
29
  {% endif %}
 
23
  {% with match=None %}
24
  {% for valor in valores %}
25
  {% if valor.nombre_palabra == palabra %}
 
26
  {{ valor.dato_valor }}
27
  {% with match=True %}{% endwith %}
28
  {% endif %}
tecnicas/templates/tecnicas/create_sesion/configuracion-panel-basic.html CHANGED
@@ -13,8 +13,8 @@
13
  <hr>
14
  <form method="post" action="" class="[&>article]:not-last:mb-4 [&>article]:not-first:pt-4 [&>article]:px-6">
15
  {% csrf_token %}
16
- <label for="form_sesion.id_tecnica.id_for_label" class="hidden">
17
- {{ form_sesion.id_tecnica }}
18
  </label>
19
 
20
  <article>
@@ -26,20 +26,45 @@
26
  {{ form_sesion.nombre_sesion }}
27
  </label>
28
  </section>
29
- <section
30
- class="flex flex-row flex-wrap justify-center gap-4 [&>label]:text-lg [&>label]:flex [&>label]:flex-col [&>label]:items-center [&>label]:px-2 [&>label]:font-medium [&>label]:tracking-wide [&>label]:[&>p]:tracking-normal [&>label]:[&>p]:text-base [&>label]:[&>p]:font-bold">
31
- <label for="{{ form_sesion.numero_productos.id_for_label }}">
32
- <p>Número de Productos:</p>
 
 
33
  {{ form_sesion.numero_productos }}
34
  </label>
35
- <label for="{{ form_sesion.numero_catadores.id_for_label }}">
36
- <p>Número de Catadores:</p>
 
 
 
 
37
  {{ form_sesion.numero_catadores }}
38
  </label>
39
- <label for="{{ form_sesion.numero_repeticiones.id_for_label }}">
40
- <p>Número de Repeticiones:</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  {{ form_sesion.numero_repeticiones }}
42
  </label>
 
43
  </section>
44
  <section class="flex justify-center items-center mt-2">
45
  <label for="{{form_sesion.instrucciones.id_for_label}}"
@@ -93,6 +118,23 @@
93
  <p>Establece número de segmentos:</p>
94
  {{ form_sesion.tamano_escala }}
95
  </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  {% if form_sesion.tamano_escala.errors %}
97
  <article
98
  class="w-fit rounded px-2 py-0.5 text-center text-white text-sm font-medium tracking-wide bg-red-500">
 
13
  <hr>
14
  <form method="post" action="" class="[&>article]:not-last:mb-4 [&>article]:not-first:pt-4 [&>article]:px-6">
15
  {% csrf_token %}
16
+ <label for="form_sesion.name_tecnica.id_for_label" class="hidden">
17
+ {{ form_sesion.name_tecnica }}
18
  </label>
19
 
20
  <article>
 
26
  {{ form_sesion.nombre_sesion }}
27
  </label>
28
  </section>
29
+ <section class="flex flex-row flex-wrap justify-center gap-4">
30
+ <label for="{{ form_sesion.numero_productos.id_for_label }}"
31
+ class="text-lg flex flex-col items-center px-2 font-medium tracking-wide">
32
+ <p class="tracking-normal text-base font-bold">
33
+ Número de Productos:
34
+ </p>
35
  {{ form_sesion.numero_productos }}
36
  </label>
37
+ {% if use_technique == 'escalas' %}
38
+ <label for="{{ form_sesion.numero_catadores.id_for_label }}"
39
+ class="text-lg flex flex-col items-center px-2 font-medium tracking-wide">
40
+ <p class="tracking-normal text-base font-bold">
41
+ Número de Catadores:
42
+ </p>
43
  {{ form_sesion.numero_catadores }}
44
  </label>
45
+ <label for="{{ form_sesion.numero_repeticiones.id_for_label }}"
46
+ class="text-lg flex flex-col items-center px-2 font-medium tracking-wide">
47
+ <p class="tracking-normal text-base font-bold">
48
+ Número de Repeticiones:
49
+ </p>
50
+ {{ form_sesion.numero_repeticiones }}
51
+ </label>
52
+ {% else %}
53
+ <label for="{{ form_sesion.numero_catadores.id_for_label }}"
54
+ class="text-lg flex flex-col items-center px-2 font-medium tracking-wide hidden">
55
+ <p class="tracking-normal text-base font-bold">
56
+ Número de Catadores:
57
+ </p>
58
+ {{ form_sesion.numero_catadores }}
59
+ </label>
60
+ <label for="{{ form_sesion.numero_repeticiones.id_for_label }}"
61
+ class="text-lg flex flex-col items-center px-2 font-medium tracking-wide hidden">
62
+ <p class="tracking-normal text-base font-bold">
63
+ Número de Repeticiones:
64
+ </p>
65
  {{ form_sesion.numero_repeticiones }}
66
  </label>
67
+ {% endif %}
68
  </section>
69
  <section class="flex justify-center items-center mt-2">
70
  <label for="{{form_sesion.instrucciones.id_for_label}}"
 
118
  <p>Establece número de segmentos:</p>
119
  {{ form_sesion.tamano_escala }}
120
  </label>
121
+ <div class="flex justify-around max-sm:flex-col gap-8 cts-options-size-scale">
122
+ <label class="flex flex-col items-center cursor-pointer">
123
+ <input type="radio" name="option_size_scale" value="5"
124
+ class="radio radio-lg checked:bg-pink-500" />
125
+ <span class="mt-2 text-xl text-gray-700 font-medium">5</span>
126
+ </label>
127
+ <label class="flex flex-col items-center cursor-pointer">
128
+ <input type="radio" name="option_size_scale" value="7"
129
+ class="radio radio-lg checked:bg-pink-500" />
130
+ <span class="mt-2 text-xl text-gray-700 font-medium">7</span>
131
+ </label>
132
+ <label class="flex flex-col items-center cursor-pointer">
133
+ <input type="radio" name="option_size_scale" value="9"
134
+ class="radio radio-lg checked:bg-pink-500" />
135
+ <span class="mt-2 text-xl text-gray-700 font-medium">9</span>
136
+ </label>
137
+ </div>
138
  {% if form_sesion.tamano_escala.errors %}
139
  <article
140
  class="w-fit rounded px-2 py-0.5 text-center text-white text-sm font-medium tracking-wide bg-red-500">
tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html CHANGED
@@ -27,12 +27,15 @@
27
  </label>
28
  {% endfor %}
29
  </article>
 
30
  <button type="button" class="cts-btn-general cts-btn-secondary btn-push uppercase"
31
  onclick="setPermutations()">
32
  regenerar ordenes
33
  </button>
 
34
  </article>
35
 
 
36
  <h2 class="text-2xl mb-2 font-bold">Ordenes</h2>
37
  <hr>
38
  <article class="w-full">
@@ -42,6 +45,7 @@
42
  </article>
43
  </section>
44
  </article>
 
45
 
46
  <hr>
47
  <article class="flex flex-col items-center gap-4">
@@ -62,11 +66,16 @@
62
  </a>
63
  </article>
64
  </form>
 
 
65
  <input type="number" value="{{ num_tester }}" class="ct-num-cata hidden" disabled>
 
66
  </article>
67
  </article>
68
  {% endblock %}
69
 
70
  {% block extra_js %}
 
71
  <script src="{% static 'js/panel-codes.js' %}"></script>
 
72
  {% endblock %}
 
27
  </label>
28
  {% endfor %}
29
  </article>
30
+ {% if use_technique == 'escalas' %}
31
  <button type="button" class="cts-btn-general cts-btn-secondary btn-push uppercase"
32
  onclick="setPermutations()">
33
  regenerar ordenes
34
  </button>
35
+ {% endif %}
36
  </article>
37
 
38
+ {% if use_technique == 'escalas' %}
39
  <h2 class="text-2xl mb-2 font-bold">Ordenes</h2>
40
  <hr>
41
  <article class="w-full">
 
45
  </article>
46
  </section>
47
  </article>
48
+ {% endif %}
49
 
50
  <hr>
51
  <article class="flex flex-col items-center gap-4">
 
66
  </a>
67
  </article>
68
  </form>
69
+
70
+ {% if use_technique == 'escalas' %}
71
  <input type="number" value="{{ num_tester }}" class="ct-num-cata hidden" disabled>
72
+ {% endif %}
73
  </article>
74
  </article>
75
  {% endblock %}
76
 
77
  {% block extra_js %}
78
+ {% if use_technique == 'escalas' %}
79
  <script src="{% static 'js/panel-codes.js' %}"></script>
80
+ {% endif %}
81
  {% endblock %}
tecnicas/templates/tecnicas/create_sesion/configuracion-panel-tags.html CHANGED
@@ -19,7 +19,8 @@
19
  <article class="p-4 flex flex-col gap-2 rounded max-sm:text-center">
20
  <h2 class="text-2xl mb-2 font-bold">Seleccion de etiquetas</h2>
21
  {% for field in form_tags %}
22
- <div class="flex max-sm:flex-col justify-center items-center gap-5 max-sm:gap-2 text-lg border-b-4 border-l-4 border-btn-secondary bg-surface-card p-1">
 
23
  <label for="{{ field.id_for_label }}"
24
  class="font-medium p-1 px-3 max-sm:w-full capitalize rounded">
25
  {{ field.label }}
@@ -44,11 +45,10 @@
44
  <button type="submit" class="cts-btn-general cts-btn-primary btn-push flex-1 w-full">
45
  Continuar
46
  </button>
47
- <a href="{% url 'cata_system:seleccion_tecnica' %}" class="flex-1 w-full">
48
- <button type="button" class="cts-btn-general cts-btn-secondary btn-push w-full">
49
- Paso anterior
50
- </button>
51
- </a>
52
  </section>
53
  <a href="{% url 'cata_system:seleccion_tecnica' %}" class="flex-1 w-full">
54
  <button type="button" class="cts-btn-general cts-btn-error btn-push w-full">
@@ -71,7 +71,8 @@
71
  <p
72
  class="ct-error-tag mt-4 font-bold bg-red-500 text-white text-md capitalize text-center rounded hidden">
73
  error</p>
74
- <article class="flex max-sm:flex-col justify-center gap-8 max-sm:gap-4 mt-4 text-white font-medium tracking-wide">
 
75
  <button type="submit" class="cts-btn-general cts-btn-primary btn-push">
76
  Agregar
77
  </button>
@@ -89,20 +90,7 @@
89
 
90
  {% block extra_js %}
91
  <script src="{% static 'js/panel-tags.js' %}"></script>
 
92
 
93
- {%if escala == "estructurada"%}
94
- <script>
95
- for (let i = 0; i < options.length; i++) {
96
- if (options.item(i).value) {
97
- values.push(options.item(i).value)
98
- }
99
- }
100
-
101
- values.sort()
102
-
103
- for (let i = 0; i < values.length; i++) {
104
- itemsSelects.item(i).value = values[i]
105
- }
106
- </script>
107
- {% endif %}
108
- {% endblock %}
 
19
  <article class="p-4 flex flex-col gap-2 rounded max-sm:text-center">
20
  <h2 class="text-2xl mb-2 font-bold">Seleccion de etiquetas</h2>
21
  {% for field in form_tags %}
22
+ <div
23
+ class="flex max-sm:flex-col justify-center items-center gap-5 max-sm:gap-2 text-lg border-b-4 border-l-4 border-btn-secondary bg-surface-card p-1">
24
  <label for="{{ field.id_for_label }}"
25
  class="font-medium p-1 px-3 max-sm:w-full capitalize rounded">
26
  {{ field.label }}
 
45
  <button type="submit" class="cts-btn-general cts-btn-primary btn-push flex-1 w-full">
46
  Continuar
47
  </button>
48
+ <button type="button" class="cts-btn-general cts-btn-secondary btn-push w-full"
49
+ onclick="window.history.back();">
50
+ Paso anterior
51
+ </button>
 
52
  </section>
53
  <a href="{% url 'cata_system:seleccion_tecnica' %}" class="flex-1 w-full">
54
  <button type="button" class="cts-btn-general cts-btn-error btn-push w-full">
 
71
  <p
72
  class="ct-error-tag mt-4 font-bold bg-red-500 text-white text-md capitalize text-center rounded hidden">
73
  error</p>
74
+ <article
75
+ class="flex max-sm:flex-col justify-center gap-8 max-sm:gap-4 mt-4 text-white font-medium tracking-wide">
76
  <button type="submit" class="cts-btn-general cts-btn-primary btn-push">
77
  Agregar
78
  </button>
 
90
 
91
  {% block extra_js %}
92
  <script src="{% static 'js/panel-tags.js' %}"></script>
93
+ {% endblock %}
94
 
95
+ <a href="{% url 'cata_system:seleccion_tecnica' %}" class="flex-1 w-full">
96
+ </a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/templates/tecnicas/create_sesion/seleccion-tecnica.html CHANGED
@@ -33,7 +33,7 @@
33
  <section
34
  class="modal-list-{{ cate }} flex flex-col text-lg bg-surface-ligt p-4 pr-6 pl-8 sm:pl-12 z-10 -translate-y-1 rounded-b-2xl *:not-last:mb-4 transition-all duration-1000 hidden w-full">
35
  {% for tecnica in tecnicas %}
36
- <a href="{% url 'cata_system:panel_configuracion_basic'%}?id_tecnica={{tecnica.id}}">
37
  <button
38
  class="cts-btn-secondary btn-push rounded-lg border-b-0 border-l-4 border-gray-700 px-3 py-2 w-full text-left font-bold uppercase">
39
  {{ tecnica.nombre_tecnica }}
 
33
  <section
34
  class="modal-list-{{ cate }} flex flex-col text-lg bg-surface-ligt p-4 pr-6 pl-8 sm:pl-12 z-10 -translate-y-1 rounded-b-2xl *:not-last:mb-4 transition-all duration-1000 hidden w-full">
35
  {% for tecnica in tecnicas %}
36
+ <a href="{% url 'cata_system:panel_configuracion_basic'%}?name_tecnica={{ tecnica }}">
37
  <button
38
  class="cts-btn-secondary btn-push rounded-lg border-b-0 border-l-4 border-gray-700 px-3 py-2 w-full text-left font-bold uppercase">
39
  {{ tecnica.nombre_tecnica }}
tecnicas/templates/tecnicas/forms_tester/convencional.html CHANGED
@@ -45,7 +45,7 @@
45
  </header>
46
 
47
  <article class="hidden">
48
- <form action="{% url 'cata_system:catador_main' %}" method="post" class="form-actions">
49
  {% csrf_token %}
50
  <input type="hidden" name="action" class="action-input">
51
  </form>
 
45
  </header>
46
 
47
  <article class="hidden">
48
+ <form action="{% url 'cata_system:catador_init_session' code_sesion=session.codigo_sesion %}" method="post" class="form-actions">
49
  {% csrf_token %}
50
  <input type="hidden" name="action" class="action-input">
51
  </form>