chartManD commited on
Commit
7f2cb8d
·
2 Parent(s): 53d2e77 7facb3f

Merge branch 'dev'

Browse files

Tablas para los datos recabados en la sesion.

Files changed (31) hide show
  1. tecnicas/controllers/models_controller/calificacion_controller.py +2 -7
  2. tecnicas/controllers/models_controller/dato_controller.py +25 -2
  3. tecnicas/controllers/models_controller/palabras_controller.py +1 -0
  4. tecnicas/controllers/models_controller/particiapacion_controller.py +30 -7
  5. tecnicas/controllers/models_controller/sesion_controller.py +23 -3
  6. tecnicas/controllers/models_controller/tecnica_controller.py +2 -2
  7. tecnicas/controllers/views_controller/detalles_sesion_controller.py +59 -13
  8. tecnicas/controllers/views_controller/main_tester_form_controller.py +15 -12
  9. tecnicas/controllers/views_controller/monitor_sesion_controller.py +52 -2
  10. tecnicas/models/dato.py +8 -2
  11. tecnicas/models/dato_valor.py +8 -4
  12. tecnicas/static/js/created-scale.js +1 -1
  13. tecnicas/static/js/finish-session.js +5 -0
  14. tecnicas/static/js/start-tester-test.js +6 -0
  15. tecnicas/templates/tecnicas/auth.html +27 -21
  16. tecnicas/templates/tecnicas/cata-login.html +45 -39
  17. tecnicas/templates/tecnicas/components/error-message.html +5 -0
  18. tecnicas/templates/tecnicas/components/table-convencional.html +47 -0
  19. tecnicas/templates/tecnicas/forms_tester/convencional.html +73 -24
  20. tecnicas/templates/tecnicas/forms_tester/main_tester.html +14 -14
  21. tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html +14 -17
  22. tecnicas/templates/tecnicas/manage_sesions/monitor-sesion.html +23 -7
  23. tecnicas/templates/tecnicas/manage_sesions/sesiones-panel.html +1 -1
  24. tecnicas/utils/__init__.py +2 -1
  25. tecnicas/utils/to_dict.py +9 -0
  26. tecnicas/views/sessions_config/create_session.py +7 -1
  27. tecnicas/views/sessions_management/session_details.py +5 -3
  28. tecnicas/views/sessions_management/session_monitor.py +32 -5
  29. tecnicas/views/tester_forms/main_tester_form.py +5 -1
  30. theme/static_src/src/styles.css +4 -0
  31. theme/static_src/tailwind.config.js +1 -1
tecnicas/controllers/models_controller/calificacion_controller.py CHANGED
@@ -48,14 +48,9 @@ class CalificacionController():
48
  if not repetition:
49
  return {"error": "sin datos calficados aun"}
50
 
51
- data_rating = {}
52
 
53
- for i in range(repetition):
54
- response_data = Calificacion.objects.filter(
55
- id_tecnica=technique, num_repeticion=i+1)
56
- data_rating[f"repeticion_{i+1}"] = response_data
57
-
58
- return data_rating
59
 
60
  @staticmethod
61
  def getRatings(
 
48
  if not repetition:
49
  return {"error": "sin datos calficados aun"}
50
 
51
+ ratings = list(Calificacion.objects.filter(id_tecnica=technique))
52
 
53
+ return ratings
 
 
 
 
 
54
 
55
  @staticmethod
56
  def getRatings(
tecnicas/controllers/models_controller/dato_controller.py CHANGED
@@ -1,6 +1,7 @@
1
- from ...models import Calificacion, Dato, Palabra, ValorDecimal, ValorBooleano
2
  from ...utils import controller_error, getId
3
  from django.core.exceptions import ValidationError
 
4
 
5
 
6
  class DatoController():
@@ -36,7 +37,7 @@ class DatoController():
36
 
37
  def setInstanceValue(self):
38
  technique = self.data.id_calificacion.id_tecnica
39
-
40
  if technique.tipo_tecnica == "cata":
41
  self.value_data = ValorBooleano(
42
  id_dato=self.data,
@@ -72,3 +73,25 @@ class DatoController():
72
  id_calificacion_id__in=ids_ratings))
73
 
74
  return recoreded_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Calificacion, Dato, Palabra, ValorDecimal, ValorBooleano, Tecnica
2
  from ...utils import controller_error, getId
3
  from django.core.exceptions import ValidationError
4
+ from django.db.models import F
5
 
6
 
7
  class DatoController():
 
37
 
38
  def setInstanceValue(self):
39
  technique = self.data.id_calificacion.id_tecnica
40
+
41
  if technique.tipo_tecnica == "cata":
42
  self.value_data = ValorBooleano(
43
  id_dato=self.data,
 
73
  id_calificacion_id__in=ids_ratings))
74
 
75
  return recoreded_data
76
+
77
+ @staticmethod
78
+ def getWordValuesForConvecional(technique: Tecnica, ratings: list[Calificacion]):
79
+ model = ValorBooleano if technique.tipo_tecnica == "cata" else ValorDecimal
80
+
81
+ ids_ratings = [rat.id for rat in ratings]
82
+
83
+ result = (
84
+ model.objects
85
+ .filter(id_dato__id_calificacion_id__in=ids_ratings)
86
+ .values(
87
+ nombre_palabra=F("id_dato__id_palabra__nombre_palabra"),
88
+ repeticion=F("id_dato__id_calificacion__num_repeticion"),
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
+ )
96
+
97
+ return list(result)
tecnicas/controllers/models_controller/palabras_controller.py CHANGED
@@ -18,6 +18,7 @@ class PalabrasController():
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
  return self.words
22
 
23
  @staticmethod
 
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
tecnicas/controllers/models_controller/particiapacion_controller.py CHANGED
@@ -1,4 +1,4 @@
1
- from ...models import Participacion, Tecnica
2
  from ...utils import controller_error
3
 
4
 
@@ -13,7 +13,7 @@ class ParticipacionController():
13
  return participation
14
  except Participacion.DoesNotExist:
15
  return controller_error("No se ha encontrado la participación")
16
-
17
  @staticmethod
18
  def finishSession(id_participation: int):
19
  try:
@@ -24,7 +24,7 @@ class ParticipacionController():
24
  return participation
25
  except Participacion.DoesNotExist:
26
  return controller_error("No se ha encontrado la participación")
27
-
28
  @staticmethod
29
  def outSession(id_participation: int):
30
  try:
@@ -34,9 +34,33 @@ class ParticipacionController():
34
  return participation
35
  except Participacion.DoesNotExist:
36
  return controller_error("No se ha encontrado la participación")
37
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  @staticmethod
39
- def getParticipationsInTechinique(technique: Tecnica| int):
40
  filters = {}
41
 
42
  if isinstance(technique, int):
@@ -44,5 +68,4 @@ class ParticipacionController():
44
  else:
45
  filters["tecnica"] = technique
46
 
47
- participations = list(Participacion.objects.filter(**filters))
48
- return participations
 
1
+ from ...models import Participacion, Tecnica, SesionSensorial
2
  from ...utils import controller_error
3
 
4
 
 
13
  return participation
14
  except Participacion.DoesNotExist:
15
  return controller_error("No se ha encontrado la participación")
16
+
17
  @staticmethod
18
  def finishSession(id_participation: int):
19
  try:
 
24
  return participation
25
  except Participacion.DoesNotExist:
26
  return controller_error("No se ha encontrado la participación")
27
+
28
  @staticmethod
29
  def outSession(id_participation: int):
30
  try:
 
34
  return participation
35
  except Participacion.DoesNotExist:
36
  return controller_error("No se ha encontrado la participación")
37
+
38
+ @staticmethod
39
+ def outAllInSession(session: SesionSensorial | str):
40
+ try:
41
+ if isinstance(session, str):
42
+ use_session = SesionSensorial.objects.get(
43
+ codigo_sesion=session)
44
+ else:
45
+ use_session = session
46
+
47
+ participations = Participacion.objects.filter(
48
+ tecnica=use_session.tecnica)
49
+
50
+ if not participations.exists():
51
+ message = "No se encontraron participaciones en la sesión"
52
+ return (False, message)
53
+
54
+ participations.update(finalizado=False)
55
+
56
+ message = "Participaciones actualizadas a finalizadas"
57
+ return (True, message)
58
+ except Exception as e:
59
+ print(f"Error al actualizar las participaciones: {str(e)}")
60
+ return (False, "Error al actualizar las participaciones")
61
+
62
  @staticmethod
63
+ def getParticipationsInTechinique(technique: Tecnica | int):
64
  filters = {}
65
 
66
  if isinstance(technique, int):
 
68
  else:
69
  filters["tecnica"] = technique
70
 
71
+ return list(Participacion.objects.filter(**filters))
 
tecnicas/controllers/models_controller/sesion_controller.py CHANGED
@@ -1,7 +1,8 @@
1
  from django.db import DatabaseError
2
  from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
3
- from ...models import Tecnica, Presentador, SesionSensorial
4
- from ...utils import controller_error
 
5
 
6
 
7
  class SesionController():
@@ -91,7 +92,7 @@ class SesionController():
91
  return session
92
  except SesionSensorial.DoesNotExist:
93
  return controller_error("La sesión ya no existe")
94
-
95
  @staticmethod
96
  def getSessionByCode(code: str):
97
  try:
@@ -111,3 +112,22 @@ class SesionController():
111
  return number_sessions/9
112
  except Presentador.DoesNotExist:
113
  return controller_error("presentador invalido")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.db import DatabaseError
2
  from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
3
+ from tecnicas.models import Tecnica, Presentador, SesionSensorial
4
+ from tecnicas.utils import controller_error
5
+ from ..models_controller.particiapacion_controller import ParticipacionController
6
 
7
 
8
  class SesionController():
 
92
  return session
93
  except SesionSensorial.DoesNotExist:
94
  return controller_error("La sesión ya no existe")
95
+
96
  @staticmethod
97
  def getSessionByCode(code: str):
98
  try:
 
112
  return number_sessions/9
113
  except Presentador.DoesNotExist:
114
  return controller_error("presentador invalido")
115
+
116
+ @staticmethod
117
+ def finishRepetion(session: SesionSensorial | str):
118
+ if isinstance(session, str):
119
+ use_session = SesionSensorial.objects.get(codigo_sesion=session)
120
+ else:
121
+ use_session = session
122
+
123
+ (is_update_participations,
124
+ message) = ParticipacionController.outAllInSession(use_session)
125
+
126
+ if not is_update_participations:
127
+ return controller_error(message)
128
+
129
+ use_session.activo = False
130
+
131
+ use_session.save()
132
+
133
+ return session
tecnicas/controllers/models_controller/tecnica_controller.py CHANGED
@@ -1,6 +1,6 @@
1
- from ...models import TipoTecnica, CategoriaTecnica, Tecnica, EstiloPalabra
2
  from django.db import DatabaseError
3
- from ...utils import controller_error
4
 
5
 
6
  class TecnicaController():
 
1
+ from tecnicas.models import TipoTecnica, CategoriaTecnica, Tecnica, EstiloPalabra
2
  from django.db import DatabaseError
3
+ from tecnicas.utils import controller_error
4
 
5
 
6
  class TecnicaController():
tecnicas/controllers/views_controller/detalles_sesion_controller.py CHANGED
@@ -1,24 +1,70 @@
1
- from ...models import SesionSensorial, Presentador, Tecnica
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  from .. import CalificacionController, PalabrasController
3
  from ...utils import controller_error
 
 
 
4
 
5
 
6
  class DetallesSesionController():
7
- @staticmethod
8
- def getContextForView(session_code: str):
9
- context = {}
 
 
10
 
11
- session = SesionSensorial.objects.get(codigo_sesion=session_code)
12
- context["sesion"] = session
13
 
14
- words = PalabrasController.getWordsInTechnique(session.tecnica)
15
- context["palabras"] = words
 
16
 
17
- rating = CalificacionController.getRatingsByTechnique(
18
- technique=session.tecnica)
19
- context["calificaciones"] = rating
20
 
21
- return context
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  @staticmethod
24
  def startRepetition(session_code: str, username: str):
@@ -45,5 +91,5 @@ class DetallesSesionController():
45
 
46
  technique.save()
47
  session.save()
48
-
49
  return session
 
1
+ '''
2
+
3
+ Para Tecnicas Convencionales, CATA, RATA, Escala Hedonica
4
+ Encabezados de como deben de aparecer los datos por repeticion
5
+
6
+ | Repeticion: R
7
+ | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
8
+
9
+ Encabezados de como deben de aparecer los datos juntos
10
+
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(
40
+ technique=self.session.tecnica)
41
+
42
+ if isinstance(ratings, dict) or not ratings:
43
+ self.context["calificaciones"] = ratings_for_repetition
44
+ self.context["existen_calificaciones"] = False
45
+ return self.context
46
+
47
+ data = DatoController.getWordValuesForConvecional(
48
+ ratings=ratings, technique=self.session.tecnica)
49
+
50
+ ratings_for_repetition = defaultdict(
51
+ lambda: defaultdict(lambda: defaultdict(list)))
52
+
53
+ for item in data:
54
+ user = item["usuarioCatador"]
55
+ rep = item["repeticion"]
56
+ prod = item["producto_code"]
57
+
58
+ ratings_for_repetition[rep][user][prod].append({
59
+ "nombre_palabra": item["nombre_palabra"],
60
+ "dato_valor": item["dato_valor"]
61
+ })
62
+
63
+ self.context["calificaciones"] = defaultdict_to_dict(
64
+ ratings_for_repetition)
65
+ self.context["existen_calificaciones"] = True
66
+
67
+ return self.context
68
 
69
  @staticmethod
70
  def startRepetition(session_code: str, username: str):
 
91
 
92
  technique.save()
93
  session.save()
94
+
95
  return session
tecnicas/controllers/views_controller/main_tester_form_controller.py CHANGED
@@ -1,4 +1,4 @@
1
- from ...models import Catador, SesionSensorial, Orden, Participacion, Producto, EsAtributo, Calificacion, EsVocabulario
2
  from ...utils import controller_error, shuffleArray
3
  from django.db import transaction
4
 
@@ -45,12 +45,17 @@ class MainTesterFormController():
45
  return controller_error("Catador sin orden")
46
 
47
  def isEndedSession(self, id_participation: int, repetition: int):
48
- if not self.order or not id_participation:
49
- return controller_error("Se requieren datos para comprobar la finalización")
50
-
51
  try:
52
  participation = Participacion.objects.get(id=id_participation)
53
 
 
 
 
 
 
 
 
 
54
  if participation.finalizado:
55
  num_products = Producto.objects.filter(
56
  id_tecnica=self.session.tecnica).count()
@@ -60,20 +65,18 @@ class MainTesterFormController():
60
  num_words: int
61
 
62
  if style_words.nombre_estilo == "atributos":
63
- e_atribues = EsAtributo.objects.get(
64
- id_tecnica=self.session.tecnica)
65
- num_words = e_atribues.palabras.count()
66
  elif style_words.nombre_estilo == "vocabulario":
67
- e_vocabulary = EsVocabulario.objects.get(
68
- id_tecnica=self.session.tecnica)
69
- num_words = e_vocabulary.id_vocabulario.palabras.count()
70
 
71
  num_ratings_now = Calificacion.objects.filter(
72
  id_tecnica=self.session.tecnica, id_catador=self.tester, num_repeticion=repetition).count()
73
 
74
- num_ratings_max_by_tester = num_products * num_words
75
 
76
- return not num_ratings_now <= num_ratings_max_by_tester
77
  else:
78
  return participation.finalizado
79
  except Participacion.DoesNotExist:
 
1
+ from tecnicas.models import Catador, SesionSensorial, Orden, Participacion, Producto, EsAtributo, Calificacion, EsVocabulario
2
  from ...utils import controller_error, shuffleArray
3
  from django.db import transaction
4
 
 
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
+ #
53
+ # Si numero_calificaciones_esperadas = num_productos * num_palabras
54
+ # Es igual a numero_calificaciones_actuales en la repetcion R
55
+ # Ha terminado la repeticion
56
+ #
57
+ # ////////////////////////////////////////////////////////////// #
58
+
59
  if participation.finalizado:
60
  num_products = Producto.objects.filter(
61
  id_tecnica=self.session.tecnica).count()
 
65
  num_words: int
66
 
67
  if style_words.nombre_estilo == "atributos":
68
+ num_words = EsAtributo.objects.get(
69
+ id_tecnica=self.session.tecnica).palabras.count()
 
70
  elif style_words.nombre_estilo == "vocabulario":
71
+ num_words = EsVocabulario.objects.get(
72
+ id_tecnica=self.session.tecnica).id_vocabulario.palabras.count()
 
73
 
74
  num_ratings_now = Calificacion.objects.filter(
75
  id_tecnica=self.session.tecnica, id_catador=self.tester, num_repeticion=repetition).count()
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:
tecnicas/controllers/views_controller/monitor_sesion_controller.py CHANGED
@@ -1,5 +1,5 @@
1
- from tecnicas.models import SesionSensorial
2
- from tecnicas.controllers import ParticipacionController
3
  from tecnicas.utils import controller_error
4
 
5
 
@@ -7,6 +7,10 @@ class MonitorSesionController():
7
  def __init__(self, session_code: str):
8
  self.code_session = session_code
9
 
 
 
 
 
10
  def monitorView(self):
11
  try:
12
  self.sensorial_session = SesionSensorial.objects.select_related(
@@ -30,3 +34,49 @@ class MonitorSesionController():
30
  }
31
 
32
  return context
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
 
 
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(
 
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
+
62
+ all_participations = ParticipacionController.getParticipationsInTechinique(
63
+ technique=technique)
64
+
65
+ if len(all_participations) < technique.limite_catadores:
66
+ return (False, "No se ha alcanzado el número máximo de Catadores")
67
+
68
+ for particiapation in all_participations:
69
+ num_ratings_now = Calificacion.objects.filter(
70
+ id_tecnica=technique, id_catador=particiapation.catador, num_repeticion=technique.repeticion).count()
71
+
72
+ if num_ratings_now < expected_ratings_repetition:
73
+ return (False, "No todos los catadores han finalizado su evaluación")
74
+
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
tecnicas/models/dato.py CHANGED
@@ -3,6 +3,12 @@ from django.db import models
3
  from .palabra import Palabra
4
  from .calificacion import Calificacion
5
 
 
6
  class Dato(models.Model):
7
- id_palabra = models.ForeignKey(Palabra, on_delete=models.CASCADE, related_name="dato_palabra")
8
- id_calificacion = models.ForeignKey(Calificacion, on_delete=models.CASCADE, related_name="dato_calificacion")
 
 
 
 
 
 
3
  from .palabra import Palabra
4
  from .calificacion import Calificacion
5
 
6
+
7
  class Dato(models.Model):
8
+ id_palabra = models.ForeignKey(
9
+ Palabra, on_delete=models.CASCADE, related_name="dato_palabra")
10
+ id_calificacion = models.ForeignKey(
11
+ Calificacion, on_delete=models.CASCADE, related_name="dato_calificacion")
12
+
13
+ def __str__(self):
14
+ return f"{self.id_palabra.nombre_palabra} - {self.id_calificacion.id_producto.codigoProducto} - {self.id_calificacion.id_catador.usuarioCatador}"
tecnicas/models/dato_valor.py CHANGED
@@ -2,16 +2,20 @@ from django.db import models
2
 
3
  from .dato import Dato
4
 
 
5
  class ValorDecimal(models.Model):
6
- id_dato = models.OneToOneField(Dato, on_delete=models.CASCADE, related_name="dato_decimal")
 
7
  valor = models.FloatField()
8
 
9
  def __str__(self):
10
- return f"{self.id} - {self.id_dato.id_palabra}: {self.valor}"
 
11
 
12
  class ValorBooleano(models.Model):
13
- id_dato = models.OneToOneField(Dato, on_delete=models.CASCADE, related_name="dato_boolean")
 
14
  valor = models.BooleanField()
15
 
16
  def __str__(self):
17
- return f"{self.id} - {self.id_dato.id_palabra}: {self.valor}"
 
2
 
3
  from .dato import Dato
4
 
5
+
6
  class ValorDecimal(models.Model):
7
+ id_dato = models.OneToOneField(
8
+ Dato, on_delete=models.CASCADE, related_name="dato_decimal")
9
  valor = models.FloatField()
10
 
11
  def __str__(self):
12
+ return f"{self.id} - {self.id_dato.id_palabra}: {self.valor} - {self.id_dato.id_calificacion.id_catador.usuarioCatador}"
13
+
14
 
15
  class ValorBooleano(models.Model):
16
+ id_dato = models.OneToOneField(
17
+ Dato, on_delete=models.CASCADE, related_name="dato_boolean")
18
  valor = models.BooleanField()
19
 
20
  def __str__(self):
21
+ return f"{self.id} - {self.id_dato.id_palabra}: {self.valor} - {self.id_dato.id_calificacion.id_catador.usuarioCatador}"
tecnicas/static/js/created-scale.js CHANGED
@@ -117,7 +117,7 @@ function addBtnNextWord() {
117
 
118
  const btnNesxtWord = document.createElement("button");
119
  btnNesxtWord.classList.add(...BTN_CLASS_STYLE, "ct-btn-next-word");
120
- btnNesxtWord.textContent = "Siguiente palabra";
121
  btnNesxtWord.addEventListener("click", nextWord);
122
 
123
  const articleContainer = document.createElement("article");
 
117
 
118
  const btnNesxtWord = document.createElement("button");
119
  btnNesxtWord.classList.add(...BTN_CLASS_STYLE, "ct-btn-next-word");
120
+ btnNesxtWord.textContent = "Siguiente producto";
121
  btnNesxtWord.addEventListener("click", nextWord);
122
 
123
  const articleContainer = document.createElement("article");
tecnicas/static/js/finish-session.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ function finishSession() {
2
+ const form = document.querySelector(".action-form");
3
+ form.querySelector("input").value = "finish_session";
4
+ form.submit();
5
+ }
tecnicas/static/js/start-tester-test.js CHANGED
@@ -2,3 +2,9 @@ function startTest() {
2
  const actionForms = document.querySelector(".ct-action-form");
3
  actionForms.submit();
4
  }
 
 
 
 
 
 
 
2
  const actionForms = document.querySelector(".ct-action-form");
3
  actionForms.submit();
4
  }
5
+
6
+ function closeSession() {
7
+ const actionForms = document.querySelector(".ct-action-form");
8
+ actionForms.querySelector(".action-option").value = "close_session"
9
+ actionForms.submit();
10
+ }
tecnicas/templates/tecnicas/auth.html CHANGED
@@ -3,29 +3,35 @@
3
  {% block title %}Login{% endblock %}
4
 
5
  {% block content %}
6
- <article class="w-full h-full flex flex-col justify-center items-center bg-gray-600">
7
- <form action="/auth" method="post" class="bg-gray-200 w-xl p-8 rounded-xl">
8
- <header class="text-center">
9
- <h1 class="text-5xl font-bold">Cateo System</h1>
10
- <p class="text-2xl font-medium mt-3">Presentador</p>
11
- </header>
12
- <section class="flex flex-col gap-6 items-center w-full mt-5">
13
- <label for="id">
14
- <input type="text" name="id" id="id" placeholder="Ingrese Indentifiacion"
15
- class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700" required>
16
- </label>
 
 
17
 
18
- <label for="id">
19
- <input type="text" name="id" id="id" placeholder="Ingrese Contraseña"
20
- class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700" required>
21
- </label>
 
22
 
23
- <section class="flex flex-row flex-wrap gap-4 w-f ull justify-center">
24
- <button type="submit"
25
- class="text-white bg-blue-600 hover:bg-blue-700 active:outline-none active:ring-4 active:ring-blue-300 font-medium rounded-xl text-xl px-8 py-2 text-center uppercase">Ingresar</button>
26
- </section>
 
 
27
 
28
- </section>
29
- </form>
 
30
  </article>
31
  {% endblock %}
 
3
  {% block title %}Login{% endblock %}
4
 
5
  {% block content %}
6
+ <article class="cts-container-main">
7
+ <article class="cts-wrap-content">
8
+ <form action="" method="post" class="p-8 rounded-xl text-black">
9
+ <header class="text-center">
10
+ <h1 class="text-5xl max-sm:text-3xl font-bold">Cata System</h1>
11
+ <p class="text-2xl max-sm:text-xl font-semibold mt-3">Presentador</p>
12
+ </header>
13
+ <section class="flex flex-col gap-6 items-center w-full mt-5">
14
+ <label for="id">
15
+ <input type="text" name="id" id="id" placeholder="Ingrese Indentifiacion"
16
+ 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"
17
+ required>
18
+ </label>
19
 
20
+ <label for="id">
21
+ <input type="text" name="id" id="id" placeholder="Ingrese Contraseña"
22
+ 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"
23
+ required>
24
+ </label>
25
 
26
+ <section class="flex flex-row flex-wrap gap-4 w-f ull justify-center">
27
+ <button type="submit"
28
+ class="cts-btn-general cts-btn-primary btn-push uppercase lg:w-sm">
29
+ Ingresar
30
+ </button>
31
+ </section>
32
 
33
+ </section>
34
+ </form>
35
+ </article>
36
  </article>
37
  {% endblock %}
tecnicas/templates/tecnicas/cata-login.html CHANGED
@@ -3,50 +3,56 @@
3
  {% block title %}Login{% endblock %}
4
 
5
  {% block content %}
6
- <article class="w-full h-full flex flex-col justify-center items-center bg-gray-600">
7
- <form action="" method="post" class="bg-gray-200 w-xl p-8 rounded-xl">
8
- {% csrf_token %}
9
- <header class="text-center">
10
- <h1 class="text-5xl font-bold">Cateo System</h1>
11
- <p class="text-2xl font-medium mt-3">Catadores</p>
12
- </header>
13
-
14
- {% if error %}
15
- <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
16
- <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
17
- {{ error }}
18
- </p>
19
- </article>
20
- {% endif %}
21
-
22
- <article class="flex flex-col gap-6 items-center w-full mt-5">
23
- <label for="id">
24
- <input type="text" name="code_session" id="id" placeholder="Codigo de sesion"
25
- class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700"
26
- required>
27
- </label>
28
-
29
- <label for="id">
30
- <input type="text" name="user_tester" id="id" placeholder="Nombre de usuario"
31
- class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700"
32
- required>
33
- </label>
34
-
35
- <section class="flex flex-row flex-wrap gap-4 w-f ull justify-center">
36
- <button type="submit"
37
- class="text-white bg-blue-600 hover:bg-blue-700 active:outline-none active:ring-4 active:ring-blue-300 font-medium rounded-xl text-xl px-8 py-2 text-center uppercase">Ingresar</button>
38
- </section>
39
-
40
- </article>
41
- </form>
 
 
 
 
42
  </article>
43
  {% endblock %}
44
 
45
  {% block extra_js %}
46
  <script>
47
  const error = document.querySelector(".ct-notification-error")
48
- setTimeout(() => {
49
- error.classList.add("hidden")
50
- }, 4000)
 
 
51
  </script>
52
  {% endblock %}
 
3
  {% block title %}Login{% endblock %}
4
 
5
  {% block content %}
6
+ <article class="cts-container-main">
7
+ <article class="cts-wrap-content">
8
+ <form action="" method="post" class="p-8 rounded-xl text-black">
9
+ {% csrf_token %}
10
+ <header class="text-center">
11
+ <h1 class="text-5xl max-sm:text-3xl font-bold">Cata System</h1>
12
+ <p class="text-2xl max-sm:text-xl font-semibold mt-3">Catadores</p>
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>
36
+
37
+ <section class="flex flex-row flex-wrap gap-4 w-f ull justify-center">
38
+ <button type="submit" class="cts-btn-general cts-btn-primary btn-push uppercase lg:w-sm">
39
+ Ingresar
40
+ </button>
41
+ </section>
42
+
43
+ </article>
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 %}
tecnicas/templates/tecnicas/components/error-message.html ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <article class="bg-surface-ligt border-b-2 border-ct-success p-4 text-black rounded cts-message shadow-lg">
2
+ <p class="font-sans text-xl text-center">
3
+ {{ message }}
4
+ </p>
5
+ </article>
tecnicas/templates/tecnicas/components/table-convencional.html ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <section>
2
+ <h2 class="text-lg font-bold mb-3">Repetición {{ repeticion }}</h2>
3
+
4
+ <div class="overflow-x-auto rounded-lg border border-surface-general">
5
+ <table class="min-w-max w-full text-sm text-center border-collapse">
6
+ <thead class="bg-surface-sweet text-black font-semibold">
7
+ <tr>
8
+ <th class="py-2 px-3 border border-surface-general">Usuario</th>
9
+ <th class="py-2 px-3 border border-surface-general">Producto</th>
10
+ {% for palabra in palabras %}
11
+ <th class="py-2 px-3 border border-surface-general uppercase">{{ palabra }}</th>
12
+ {% endfor %}
13
+ </tr>
14
+ </thead>
15
+ <tbody class="bg-surface-ligt divide-y divide-gray-200">
16
+ {% for usuario, productos in catadores.items %}
17
+ {% for codigo, valores in productos.items %}
18
+ <tr>
19
+ <td class="py-2 px-3 border border-surface-general">{{ usuario }}</td>
20
+ <td class="py-2 px-3 border border-surface-general">{{ codigo }}</td>
21
+ {% for palabra in palabras %}
22
+ <td class="py-2 px-3 border border-surface-general">
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 %}
30
+ {% endfor %}
31
+ {% if not match %}0{% endif %}
32
+ {% endwith %}
33
+ </td>
34
+ {% endfor %}
35
+ </tr>
36
+ {% endfor %}
37
+ {% endfor %}
38
+ </tbody>
39
+ </table>
40
+ </div>
41
+
42
+ <div class="flex justify-end mt-3">
43
+ <button class="cts-btn-general cts-btn-primary btn-push">
44
+ Descargar CSV
45
+ </button>
46
+ </div>
47
+ </section>
tecnicas/templates/tecnicas/forms_tester/convencional.html CHANGED
@@ -27,26 +27,19 @@
27
  @media (width < 40rem) {
28
  .container-forms {
29
  width: inherit;
30
-
31
- }
32
-
33
- .container-input {
34
- overflow-x: scroll;
35
  }
36
  }
37
  </style>
38
  {% endblock %}
39
 
40
  {% block content %}
41
- <article class="w-full flex flex-col justify-center items-center bg-gray-600 mt-10 mb-10 max-sm:">
42
- <article class="flex flex-col gap-8 bg-gray-400 p-10 max-sm:py-10 max-sm:px-3 rounded container-forms">
43
  <header class="text-center flex-row w-full items-stretch flex justify-around flex-wrap gap-2">
44
- <h1 class="text-white rounded font-bold text-2xl bg-gray-600 p-4 flex-1">
45
  Sesión usando <br>técnica Convencional
46
  </h1>
47
- <button
48
- class="text-lg font-bold tracking-wider p-2 px-8 border-b-4 active:border-b-0 active:border-t-2 active:border-blue-500 border-blue-800 transition-all rounded-xl bg-blue-500 text-gray-300"
49
- onclick="exit_sesion('form-actions')">
50
  Salir de la sesión
51
  </button>
52
  </header>
@@ -68,13 +61,13 @@
68
  {% endif %}
69
 
70
  <article class="rounded flex flex-col gap-4">
71
- <section class="flex items-center justify-center bg-gray-200 p-2 rounded-lg">
72
  <p class="text-lg font-medium text-center">
73
  {{ session.tecnica.instrucciones }}
74
  </p>
75
  </section>
76
  <section class="flex items-center justify-center flex-wrap gap-4">
77
- <div class="bg-gray-200 p-2 rounded-lg flex-1">
78
  <p class="text-lg font-bold text-center">
79
  Producto:
80
  </p>
@@ -83,7 +76,7 @@
83
  <span class="hidden id-product">{{ product.id }}</span>
84
  </p>
85
  </div>
86
- <div class="bg-gray-200 p-2 rounded-lg flex-1">
87
  <p class="text-lg font-bold text-center">
88
  Repetición:
89
  </p>
@@ -101,23 +94,22 @@
101
  </article>
102
 
103
  <article
104
- class="scales-container [&>*:not(:last-child)]:mb-5 min-lg:grid min-lg:items-start grid-cols-2 gap-3 flex flex-col items-center justify-center">
105
  {% if type_scale == "continua" %}
106
  {% for word in words %}
107
  <form action="" method="post" class="form-rating-{{word}} w-full">
108
  {% csrf_token %}
109
- <article class="container-input w-full">
110
- <article class="bg-gray-200 p-6 rounded-lg mb-3 w-fit">
111
  <label for="id-range-word-{{word}}"
112
  class="text-xl font-bold tracking-wide block mb-6 first-letter:uppercase">{{ word }}</label>
113
 
114
  <span class="hidden id-word">{{ word.id }}</span>
115
 
116
  <section class="block">
117
- <div class="relative">
118
- <input id="id-range-word-{{word}}" type="range" min="0" max="1000" value="500"
119
- name="rating-word"
120
- class="h-2 bg-gray-400 rounded-lg appearance-none cursor-pointer slider"
121
  style="width: {{scale.longitud}}cm;">
122
 
123
  <div
@@ -156,15 +148,72 @@
156
  <article class="flex flex-col justify-center gap-2 items-end actions-{{word}}">
157
  <section class="flex justify-end items-center gap-2 btns-container">
158
  <button type="button" onclick="checkSendRating('{{word}}')"
159
- class="ct-btn-check-{{word}} text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-blue-500 border-blue-800 transition-all rounded-xl bg-blue-500 text-white w-fit disabled:bg-amber-600">
160
  ¿Guardar calificación?
161
  </button>
162
  <button type="button" onclick="sendRating('{{word}}')"
163
- class="ct-btn-submit-{{word}} text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-500 text-white w-fit disabled:bg-amber-600 hidden">
164
  Estoy seguro
165
  </button>
166
  <button type="button" onclick="cancelSendRating('{{word}}')"
167
- class="ct-btn-cancel-{{word}} text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-orange-500 border-orange-800 transition-all rounded-xl bg-orange-500 text-white w-fit disabled:bg-amber-600 hidden">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  Cancelar
169
  </button>
170
  </section>
 
27
  @media (width < 40rem) {
28
  .container-forms {
29
  width: inherit;
 
 
 
 
 
30
  }
31
  }
32
  </style>
33
  {% endblock %}
34
 
35
  {% block content %}
36
+ <article class="cts-container-main">
37
+ <article class="cts-wrap-content text-black">
38
  <header class="text-center flex-row w-full items-stretch flex justify-around flex-wrap gap-2">
39
+ <h1 class="rounded font-bold text-2xl bg-surface-ligt p-4 flex-1">
40
  Sesión usando <br>técnica Convencional
41
  </h1>
42
+ <button class="cts-btn-general cts-btn-error btn-push" onclick="exit_sesion('form-actions')">
 
 
43
  Salir de la sesión
44
  </button>
45
  </header>
 
61
  {% endif %}
62
 
63
  <article class="rounded flex flex-col gap-4">
64
+ <section class="flex items-center justify-center bg-surface-ligt p-2 rounded-lg">
65
  <p class="text-lg font-medium text-center">
66
  {{ session.tecnica.instrucciones }}
67
  </p>
68
  </section>
69
  <section class="flex items-center justify-center flex-wrap gap-4">
70
+ <div class="bg-surface-ligt p-2 rounded-lg flex-1">
71
  <p class="text-lg font-bold text-center">
72
  Producto:
73
  </p>
 
76
  <span class="hidden id-product">{{ product.id }}</span>
77
  </p>
78
  </div>
79
+ <div class="bg-surface-ligt p-2 rounded-lg flex-1">
80
  <p class="text-lg font-bold text-center">
81
  Repetición:
82
  </p>
 
94
  </article>
95
 
96
  <article
97
+ class="scales-container [&>*:not(:last-child)]:mb-5 min-lg:grid min-lg:items-start grid-cols-2 gap-3 justify-center items-center">
98
  {% if type_scale == "continua" %}
99
  {% for word in words %}
100
  <form action="" method="post" class="form-rating-{{word}} w-full">
101
  {% csrf_token %}
102
+ <article class="overflow-x-scroll w-full">
103
+ <article class="bg-surface-card p-6 rounded-lg mb-3 w-fit">
104
  <label for="id-range-word-{{word}}"
105
  class="text-xl font-bold tracking-wide block mb-6 first-letter:uppercase">{{ word }}</label>
106
 
107
  <span class="hidden id-word">{{ word.id }}</span>
108
 
109
  <section class="block">
110
+ <div class="relative mx-6">
111
+ <input type="range" type="range" min="0" max="1000" value="500" name="rating-word"
112
+ class="range range-md text-blue-400 [--range-bg:orange] [--range-thumb:blue] [--range-fill:0]"
 
113
  style="width: {{scale.longitud}}cm;">
114
 
115
  <div
 
148
  <article class="flex flex-col justify-center gap-2 items-end actions-{{word}}">
149
  <section class="flex justify-end items-center gap-2 btns-container">
150
  <button type="button" onclick="checkSendRating('{{word}}')"
151
+ class="ct-btn-check-{{word}} cts-btn-general-compress py-2 px-4 cts-btn-secondary btn-push">
152
  ¿Guardar calificación?
153
  </button>
154
  <button type="button" onclick="sendRating('{{word}}')"
155
+ class="ct-btn-submit-{{word}} cts-btn-general-compress py-2 px-4 cts-btn-primary btn-push hidden">
156
  Estoy seguro
157
  </button>
158
  <button type="button" onclick="cancelSendRating('{{word}}')"
159
+ class="ct-btn-cancel-{{word}} cts-btn-general-compress py-2 px-4 cts-btn-error btn-push hidden">
160
+ Cancelar
161
+ </button>
162
+ </section>
163
+ </article>
164
+ </form>
165
+ {% endfor %}
166
+ {% elif type_scale == "estructurada" %}
167
+ {% for word in words %}
168
+ <form action="" method="post" class="form-rating-{{ word }} w-full">
169
+ {% csrf_token %}
170
+ <article class="overflow-x-scroll w-full">
171
+ <article class="bg-surface-card p-6 rounded-lg mb-3 w-fit">
172
+ <label for="id-scale-word-{{ word }}"
173
+ class="text-xl font-bold tracking-wide block mb-6 first-letter:uppercase">{{ word }}</label>
174
+ <span class="hidden id-word">{{ word.id }}</span>
175
+
176
+ <section>
177
+ <div class="w-full min-w-xs">
178
+ <div class="relative mx-6">
179
+ <input type="range" min="1" max="{{scale.longitud}}" step="1" name="rating-word"
180
+ class="w-full range range-md text-blue-400 [--range-bg:orange] [--range-thumb:blue] [--range-fill:0] z-5">
181
+ <div
182
+ class="absolute top-0 left-0 w-full h-full z-10 flex justify-between pointer-events-none">
183
+ {% for tag in tags %}
184
+ <div class="w-0.5 transform -translate-x-1/2 h-full bg-red-500">
185
+ </div>
186
+ {% endfor %}
187
+ </div>
188
+ </div>
189
+
190
+ <div class="flex justify-between mt-4 text-xs gap-1">
191
+ {% for tag in tags %}
192
+ <div class="flex items-center justify-center text-center w-[70px]">
193
+ <span
194
+ class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
195
+ {{ tag.id_etiqueta }}
196
+ </span>
197
+ </div>
198
+ {% endfor %}
199
+ </div>
200
+ </div>
201
+ </section>
202
+ </article>
203
+ </article>
204
+
205
+ <article class="flex flex-col justify-center gap-2 items-end actions-{{ word }}">
206
+ <section class="flex justify-end items-center gap-2 btns-container">
207
+ <button type="button" onclick="checkSendRating('{{ word }}')"
208
+ class="ct-btn-check-{{ word }} cts-btn-general-compress py-2 px-4 cts-btn-secondary btn-push">
209
+ ¿Guardar calificación?
210
+ </button>
211
+ <button type="button" onclick="sendRating('{{ word }}')"
212
+ class="ct-btn-submit-{{ word }} cts-btn-general-compress py-2 px-4 cts-btn-primary btn-push hidden">
213
+ Estoy seguro
214
+ </button>
215
+ <button type="button" onclick="cancelSendRating('{{ word }}')"
216
+ class="ct-btn-cancel-{{ word }} cts-btn-general-compress py-2 px-4 cts-btn-error btn-push hidden">
217
  Cancelar
218
  </button>
219
  </section>
tecnicas/templates/tecnicas/forms_tester/main_tester.html CHANGED
@@ -4,16 +4,16 @@
4
  {% block title %}Detalles Sesion{% endblock %}
5
 
6
  {% block content %}
7
- <article class="w-full flex flex-col justify-center items-center bg-gray-600 mt-10 mb-10">
8
- <article class="flex flex-col gap-8 bg-gray-400 p-10 rounded-2xl">
9
  <header class="text-center flex-row w-full flex justify-around items-center flex-wrap gap-10">
10
- <h1 class="text-white rounded-xl font-bold text-2xl bg-gray-600 p-4 flex-1">
11
  Panel principal de Catadores
12
  </h1>
13
  </header>
14
 
15
  <article>
16
- <p class="text-2xl font-medium text-center bg-gray-200 p-4 rounded-lg">
17
  Información sobre la sesión en la que participa
18
  </p>
19
  </article>
@@ -31,8 +31,8 @@
31
  {% if message %}
32
  <hr>
33
 
34
- <article class="bg-blue-600 p-4 text-white rounded-xl ct-notification-error">
35
- <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
36
  {{ message }}
37
  </p>
38
  </article>
@@ -41,7 +41,7 @@
41
  <hr>
42
 
43
  <article
44
- class="text-white rounded-xl grid grid-cols-1 gap-3 text-center *:bg-gray-500 *:flex *:flex-wrap *:items-center *:justify-center *:gap-x-2 *:p-4 *:rounded-2xl">
45
  {% if session %}
46
  <section>
47
  <p class="text-xl font-bold">
@@ -99,13 +99,12 @@
99
 
100
  <article class="flex flex-wrap gap-10">
101
  {% if has_ended %}
102
- <button
103
- class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2" disabled>
104
- Finalizaste la sesión
105
- </button>
106
  {% else %}
107
  <button
108
- class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2"
109
  onclick="startTest()">
110
  Iniciar técnica
111
  <figure class="w-10">
@@ -114,7 +113,8 @@
114
  </button>
115
  {% endif %}
116
  <button
117
- class="flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-red-500 border-red-800 transition-all rounded-xl bg-red-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2">
 
118
  Salir se la sesión
119
  <figure class="w-10">
120
  <img src="{% static 'img/exit.svg' %}" alt="bote de basura" class="invert">
@@ -124,7 +124,7 @@
124
 
125
  <form action="" method="post" class="hidden ct-action-form">
126
  {% csrf_token %}
127
- <input type="hidden" name="action" value="start_posting">
128
  </form>
129
  </article>
130
  </article>
 
4
  {% block title %}Detalles Sesion{% endblock %}
5
 
6
  {% block content %}
7
+ <article class="cts-container-main">
8
+ <article class="cts-wrap-content text-black">
9
  <header class="text-center flex-row w-full flex justify-around items-center flex-wrap gap-10">
10
+ <h1 class="rounded-xl font-bold text-2xl bg-surface-ligt p-4 flex-1">
11
  Panel principal de Catadores
12
  </h1>
13
  </header>
14
 
15
  <article>
16
+ <p class="text-2xl font-medium text-center bg-surface-sweet p-4 rounded-lg">
17
  Información sobre la sesión en la que participa
18
  </p>
19
  </article>
 
31
  {% if message %}
32
  <hr>
33
 
34
+ <article class="bg-surface-alter-card p-4 rounded-xl max-w-2xl">
35
+ <p class="block text-xl antialiased font-bold text-center">
36
  {{ message }}
37
  </p>
38
  </article>
 
41
  <hr>
42
 
43
  <article
44
+ class="rounded-xl grid grid-cols-1 gap-3 text-center *:bg-surface-card *:flex *:flex-wrap *:items-center *:justify-center *:gap-x-2 *:p-4 *:rounded-2xl">
45
  {% if session %}
46
  <section>
47
  <p class="text-xl font-bold">
 
99
 
100
  <article class="flex flex-wrap gap-10">
101
  {% if has_ended %}
102
+ <div class="text-2xl font-semibold flex-1 cts-btn-secondary p-4 flex justify-center items-center rounded-lg select-none">
103
+ <p class=" text-black">Finalizaste la repetición</p>
104
+ </div>
 
105
  {% else %}
106
  <button
107
+ class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider cts-btn-general cts-btn-primary btn-push flex flex-col justify-center items-center gap-2"
108
  onclick="startTest()">
109
  Iniciar técnica
110
  <figure class="w-10">
 
113
  </button>
114
  {% endif %}
115
  <button
116
+ class="flex-1 uppercase text-lg tracking-wider cts-btn-general cts-btn-error btn-push flex flex-col justify-center items-center gap-2"
117
+ onclick="closeSession()">
118
  Salir se la sesión
119
  <figure class="w-10">
120
  <img src="{% static 'img/exit.svg' %}" alt="bote de basura" class="invert">
 
124
 
125
  <form action="" method="post" class="hidden ct-action-form">
126
  {% csrf_token %}
127
+ <input type="hidden" class="action-option" name="action" value="start_posting">
128
  </form>
129
  </article>
130
  </article>
tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html CHANGED
@@ -6,20 +6,17 @@
6
  {% block content %}
7
  <article class="cts-container-main">
8
  <article class="cts-wrap-content">
9
- <header class="text-center flex-row w-full flex justify-around items-center flex-wrap gap-10">
10
  <h1 class="text-black rounded-xl font-bold text-2xl bg-surface-card shadow-lg p-4 flex-1">
11
  Detalles de la sesión
12
  </h1>
13
- <a href="{% url 'cata_system:index' %}" class="w-fit">
14
- <button
15
- class="cts-btn-general cts-btn-error btn-push">
16
- Volver a las Sesiones
17
- </button>
18
- </a>
19
  </header>
20
 
21
  {% if error %}
22
- <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
23
  <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
24
  {{ error }}
25
  </p>
@@ -111,7 +108,7 @@
111
  </section>
112
 
113
  <section>
114
- <p class="font-medium">
115
  Rep. Max:
116
  </p>
117
  <p class="font-sans text-lg max-sm:text-base font-normal">
@@ -137,7 +134,7 @@
137
  {% for palabra in palabras %}
138
  <section>
139
  <p class="block text-black text-xl antialiased font-medium">
140
- {{ palabra.nombre_palabra }}
141
  </p>
142
  </section>
143
  {% endfor %}
@@ -182,16 +179,16 @@
182
  Datos obtenidos
183
  </p>
184
 
185
- {% if calificaciones.error %}
186
- <article class="bg-surface-card p-4 text-white rounded-xl">
187
- <p class="block font-sans text-xl antialiased font-normal uppercase tracking-wider text-center">
188
- {{ calificaciones.error }}
189
- </p>
190
  </article>
191
  {% else %}
192
- <article class="bg-surface-card p-4 text-white rounded-xl">
193
- </article>
194
  {% endif %}
 
195
  <form action="" method="post" class="form-action-session hidden">
196
  {% csrf_token %}
197
  </form>
 
6
  {% block content %}
7
  <article class="cts-container-main">
8
  <article class="cts-wrap-content">
9
+ <header class="text-center flex-row max-sm:flex-col w-full flex justify-around items-center flex-wrap gap-10">
10
  <h1 class="text-black rounded-xl font-bold text-2xl bg-surface-card shadow-lg p-4 flex-1">
11
  Detalles de la sesión
12
  </h1>
13
+ <button class="cts-btn-general cts-btn-error btn-push" onclick="window.history.back()">
14
+ Volver a las Sesiones
15
+ </button>
 
 
 
16
  </header>
17
 
18
  {% if error %}
19
+ <article class=" bg-red-600 p-4 text-white rounded-xl ct-notification-error">
20
  <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
21
  {{ error }}
22
  </p>
 
108
  </section>
109
 
110
  <section>
111
+ <p class="font-bold">
112
  Rep. Max:
113
  </p>
114
  <p class="font-sans text-lg max-sm:text-base font-normal">
 
134
  {% for palabra in palabras %}
135
  <section>
136
  <p class="block text-black text-xl antialiased font-medium">
137
+ {{ palabra }}
138
  </p>
139
  </section>
140
  {% endfor %}
 
179
  Datos obtenidos
180
  </p>
181
 
182
+ {% if existen_calificaciones %}
183
+ <article class="bg-surface-card p-4 max-sm:px-2 text-black rounded">
184
+ {% for repeticion, catadores in calificaciones.items %}
185
+ {% include "../components/table-convencional.html" with repeticion=repeticion catadores=catadores palabras=palabras %}
186
+ {% endfor %}
187
  </article>
188
  {% else %}
189
+ {% include "../components/error-message.html" with message='Sin calificaciones que mostrar aún' %}
 
190
  {% endif %}
191
+
192
  <form action="" method="post" class="form-action-session hidden">
193
  {% csrf_token %}
194
  </form>
tecnicas/templates/tecnicas/manage_sesions/monitor-sesion.html CHANGED
@@ -1,4 +1,5 @@
1
  {% extends 'tecnicas/layouts/base.html' %}
 
2
 
3
  {% block title %}Monitoreo{% endblock %}
4
 
@@ -17,22 +18,36 @@
17
  </article>
18
  {% endif %}
19
 
20
- <section class="flex flex-col sm:flex-row justify-between items-center gap-10 mb-6">
21
  <p class="text-xl text-center bg-surface-card p-4 text-black rounded-lg shadow-lg">
22
  Código de sesión:<br>
23
  <span class="font-mono text-2xl font-bold">{{ code_session }}</span>
24
  </p>
25
- <button
26
- class="uppercase text-lg max-sm:text-base tracking-wider p-4 rounded-xl bg-ct-error text-white font-bold border-red-800 btn-push">
27
- Finalizar sesión
28
- </button>
 
 
 
 
 
 
29
  </section>
30
 
 
 
 
 
 
 
 
 
 
31
  <section aria-labelledby="catadores-titulo">
32
  <article
33
  class="flex max-sm:flex-col justify-around bg-surface-card border border-gray-300 rounded-md p-3 mb-4 items-center shadow-lg">
34
- <button
35
- class="uppercase text-lg max-sm:text-base tracking-wider p-2 px-3 transition-all rounded-xl bg-btn-secondary font-bold btn-push border-pink-800"
36
  onclick="reloadPage()">
37
  Actualizar lista
38
  </button>
@@ -108,4 +123,5 @@
108
  location.reload()
109
  }
110
  </script>
 
111
  {% endblock %}
 
1
  {% extends 'tecnicas/layouts/base.html' %}
2
+ {% load static %}
3
 
4
  {% block title %}Monitoreo{% endblock %}
5
 
 
18
  </article>
19
  {% endif %}
20
 
21
+ <section class="flex flex-col sm:flex-row justify-between items-center gap-10">
22
  <p class="text-xl text-center bg-surface-card p-4 text-black rounded-lg shadow-lg">
23
  Código de sesión:<br>
24
  <span class="font-mono text-2xl font-bold">{{ code_session }}</span>
25
  </p>
26
+ <div class="flex flex-col gap-2">
27
+ <button class="uppercase cts-btn-general-compress cts-btn-secondary py-2 px-6 btn-push"
28
+ onclick="finishSession()">
29
+ Finalizar sesión
30
+ </button>
31
+ <button class="uppercase cts-btn-general-compress cts-btn-error py-2 px-6 btn-push"
32
+ onclick="window.history.back()">
33
+ Regresar
34
+ </button>
35
+ </div>
36
  </section>
37
 
38
+ <form action="" method="post" class="hidden action-form">
39
+ <input type="hidden" name="action">
40
+ {% csrf_token %}
41
+ </form>
42
+
43
+ {% if message %}
44
+ {% include "../components/error-message.html" with message=message %}
45
+ {% endif %}
46
+
47
  <section aria-labelledby="catadores-titulo">
48
  <article
49
  class="flex max-sm:flex-col justify-around bg-surface-card border border-gray-300 rounded-md p-3 mb-4 items-center shadow-lg">
50
+ <button class="uppercase cts-btn-general-compress cts-btn-tertiary py-2 px-6 btn-push"
 
51
  onclick="reloadPage()">
52
  Actualizar lista
53
  </button>
 
123
  location.reload()
124
  }
125
  </script>
126
+ <script src="{% static 'js/finish-session.js' %}"></script>
127
  {% endblock %}
tecnicas/templates/tecnicas/manage_sesions/sesiones-panel.html CHANGED
@@ -22,7 +22,7 @@
22
  <section class="grid grid-cols-3 max-lg:grid-cols-2 max-sm:grid-cols-1 w-full gap-4 justify-center">
23
  {% for sesion in sessions %}
24
  <div class="card bg-surface-card shadow-lg text-black sm:max-w-80 w-full flex justify-center items-center">
25
- <div class="card-body flex flex-col justify-between">
26
  <div class="flex flex-col gap-1 [&>*]:first:text-xl [&>*]:first:font-bold [&>*]:first:border-b">
27
  {% if sesion.nombre_sesion %}
28
  <h2>{{ sesion.nombre_sesion }}</h2>
 
22
  <section class="grid grid-cols-3 max-lg:grid-cols-2 max-sm:grid-cols-1 w-full gap-4 justify-center">
23
  {% for sesion in sessions %}
24
  <div class="card bg-surface-card shadow-lg text-black sm:max-w-80 w-full flex justify-center items-center">
25
+ <div class="card-body flex flex-col justify-between w-full">
26
  <div class="flex flex-col gap-1 [&>*]:first:text-xl [&>*]:first:font-bold [&>*]:first:border-b">
27
  {% if sesion.nombre_sesion %}
28
  <h2>{{ sesion.nombre_sesion }}</h2>
tecnicas/utils/__init__.py CHANGED
@@ -2,4 +2,5 @@ from .code_generate import generarCodigo
2
  from .code_generate import generarCodigos
3
  from .personal_errors import general_error, controller_error
4
  from .shuffle_arrays import shuffleArray
5
- from .general_controllers import getId
 
 
2
  from .code_generate import generarCodigos
3
  from .personal_errors import general_error, controller_error
4
  from .shuffle_arrays import shuffleArray
5
+ from .general_controllers import getId
6
+ from .to_dict import defaultdict_to_dict
tecnicas/utils/to_dict.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from collections import defaultdict
2
+
3
+
4
+ def defaultdict_to_dict(d):
5
+ if isinstance(d, defaultdict):
6
+ d = {k: defaultdict_to_dict(v) for k, v in d.items()}
7
+ elif isinstance(d, dict):
8
+ d = {k: defaultdict_to_dict(v) for k, v in d.items()}
9
+ return d
tecnicas/views/sessions_config/create_session.py CHANGED
@@ -118,7 +118,7 @@ def createSession(req: HttpRequest):
118
  words_using = style_controller.relatedWords()
119
  if isinstance(words_using, dict):
120
  controllerTechnique.deleteTechnique()
121
- return general_error("error")
122
 
123
  # //////////////////////////////////////////////////////// #
124
  #
@@ -148,6 +148,12 @@ def createSession(req: HttpRequest):
148
  }
149
  }
150
 
 
 
 
 
 
 
151
  keys_forms = [
152
  "form_basic",
153
  "form_tags",
 
118
  words_using = style_controller.relatedWords()
119
  if isinstance(words_using, dict):
120
  controllerTechnique.deleteTechnique()
121
+ return general_error(words_using["error"])
122
 
123
  # //////////////////////////////////////////////////////// #
124
  #
 
148
  }
149
  }
150
 
151
+ # /////////////////////////////// #
152
+ #
153
+ # Final step: Delete session keys #
154
+ #
155
+ # /////////////////////////////// #
156
+
157
  keys_forms = [
158
  "form_basic",
159
  "form_tags",
tecnicas/views/sessions_management/session_details.py CHANGED
@@ -5,19 +5,21 @@ from ...controllers import DetallesSesionController
5
 
6
 
7
  def sessionDetails(req: HttpRequest, session_code: str):
8
- context = DetallesSesionController.getContextForView(session_code)
 
9
 
10
  if req.method == "GET":
 
11
  return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
12
  elif req.method == "POST":
13
  if req.POST["action"] == "start_session":
14
  response = DetallesSesionController.startRepetition(
15
  session_code=session_code, username=req.POST["username"])
16
  if isinstance(response, dict):
 
17
  context["error"] = response["error"]
18
  return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
19
- context["message"] = "La sesión ha iniciado"
20
- return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
21
  elif req.POST.get("action") == "delete_session":
22
  pass
23
  elif req.POST.get("action") == "monitor_session":
 
5
 
6
 
7
  def sessionDetails(req: HttpRequest, session_code: str):
8
+ controller_view = DetallesSesionController(session_code)
9
+ context = controller_view.getContextForView()
10
 
11
  if req.method == "GET":
12
+ context = controller_view.getContextWithData()
13
  return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
14
  elif req.method == "POST":
15
  if req.POST["action"] == "start_session":
16
  response = DetallesSesionController.startRepetition(
17
  session_code=session_code, username=req.POST["username"])
18
  if isinstance(response, dict):
19
+ context = controller_view.getContextWithData()
20
  context["error"] = response["error"]
21
  return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
22
+ return redirect(reverse("cata_system:monitor_sesion"))
 
23
  elif req.POST.get("action") == "delete_session":
24
  pass
25
  elif req.POST.get("action") == "monitor_session":
tecnicas/views/sessions_management/session_monitor.py CHANGED
@@ -1,17 +1,44 @@
 
 
 
 
 
 
1
  from django.http import HttpRequest, JsonResponse
2
- from django.shortcuts import render
 
3
  from tecnicas.controllers import MonitorSesionController
 
4
 
5
 
6
  def sessionMonitor(req: HttpRequest, session_code: str):
7
- if req.method == "GET":
8
- controll_view = MonitorSesionController(session_code)
9
 
10
- context = controll_view.monitorView()
11
  if "error" in context:
12
  return render(req, "tecnicas/manage_sesions/monitor-sesion.html", context)
13
-
14
  context["code_session"] = session_code
15
  return render(req, "tecnicas/manage_sesions/monitor-sesion.html", context)
 
 
 
 
 
 
16
  else:
17
  return JsonResponse({"error": "Método no permitido"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ Para finalizar la sesion se debe realizar lo siguiente
3
+ # Obtener todas las participaciones
4
+
5
+ '''
6
+
7
  from django.http import HttpRequest, JsonResponse
8
+ from django.shortcuts import render, redirect
9
+ from django.urls import reverse
10
  from tecnicas.controllers import MonitorSesionController
11
+ from tecnicas.utils import general_error
12
 
13
 
14
  def sessionMonitor(req: HttpRequest, session_code: str):
15
+ controll_view = MonitorSesionController(session_code)
16
+ context = controll_view.monitorView()
17
 
18
+ if req.method == "GET":
19
  if "error" in context:
20
  return render(req, "tecnicas/manage_sesions/monitor-sesion.html", context)
21
+
22
  context["code_session"] = session_code
23
  return render(req, "tecnicas/manage_sesions/monitor-sesion.html", context)
24
+ elif req.method == "POST":
25
+ action = req.POST["action"]
26
+ if action == "finish_session":
27
+ return actionFinishSession(context=context, session_code=session_code, controll_view=controll_view, req=req)
28
+ else:
29
+ return general_error("No se ha especificado la acción")
30
  else:
31
  return JsonResponse({"error": "Método no permitido"})
32
+
33
+
34
+ def actionFinishSession(context: dict, session_code: str, controll_view: MonitorSesionController, req: HttpRequest):
35
+ context["code_session"] = session_code
36
+ (is_all_end, message) = controll_view.checkAllParticipantsEnded()
37
+ context["message"] = message
38
+ if not is_all_end:
39
+ return render(req, "tecnicas/manage_sesions/monitor-sesion.html", context)
40
+ response = controll_view.finishSession()
41
+ if isinstance(response, dict):
42
+ context["message"] = response["error"]
43
+ return render(req, "tecnicas/manage_sesions/monitor-sesion.html", context)
44
+ return redirect(reverse("cata_system:detalles_sesion", kwargs={"session_code": session_code}))
tecnicas/views/tester_forms/main_tester_form.py CHANGED
@@ -20,7 +20,11 @@ def mainTesterForm(req: HttpRequest):
20
 
21
  if not isinstance(order, dict):
22
  req.session["id_order"] = order.id
23
- if view_controller.isEndedSession(id_participation=req.session["id_participation"], repetition=session.tecnica.repeticion):
 
 
 
 
24
  context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
25
  context["has_ended"] = True
26
 
 
20
 
21
  if not isinstance(order, dict):
22
  req.session["id_order"] = order.id
23
+ is_end = view_controller.isEndedSession(
24
+ id_participation=req.session["id_participation"], repetition=session.tecnica.repeticion)
25
+
26
+ if is_end:
27
+ print("Estoy dentro y termine [0_0]")
28
  context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
29
  context["has_ended"] = True
30
 
theme/static_src/src/styles.css CHANGED
@@ -14,6 +14,10 @@
14
  @apply font-bold rounded-lg text-xl p-4 text-center;
15
  }
16
 
 
 
 
 
17
  .cts-btn-primary {
18
  @apply text-white bg-btn-primary border-green-800;
19
  }
 
14
  @apply font-bold rounded-lg text-xl p-4 text-center;
15
  }
16
 
17
+ .cts-btn-general-compress {
18
+ @apply font-bold rounded-lg text-xl text-center;
19
+ }
20
+
21
  .cts-btn-primary {
22
  @apply text-white bg-btn-primary border-green-800;
23
  }
theme/static_src/tailwind.config.js CHANGED
@@ -16,7 +16,7 @@ module.exports = {
16
  "surface-alter-card": "#91C4C3",
17
  "btn-primary": "#4CAF50",
18
  "btn-secondary": "#E45A92",
19
- "btn-tertiary": "#CCECC0",
20
  "ct-success": "#2E7D32",
21
  "ct-error": "#E62727",
22
  },
 
16
  "surface-alter-card": "#91C4C3",
17
  "btn-primary": "#4CAF50",
18
  "btn-secondary": "#E45A92",
19
+ "btn-tertiary": "#88EE88",
20
  "ct-success": "#2E7D32",
21
  "ct-error": "#E62727",
22
  },