chartManD commited on
Commit
0e5431a
·
2 Parent(s): b79118c 9c5ce85

Resolucion de conflicos con 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. cata_system/settings.py +1 -1
  2. docker-compose.yml +7 -0
  3. tecnicas/admin.py +2 -1
  4. tecnicas/controllers/__init__.py +13 -7
  5. tecnicas/controllers/models_controller/calificacion_controller.py +42 -55
  6. tecnicas/controllers/models_controller/dato_controller.py +13 -14
  7. tecnicas/controllers/models_controller/palabras_controller.py +7 -8
  8. tecnicas/controllers/models_controller/particiapacion_controller.py +6 -9
  9. tecnicas/controllers/models_controller/sesion_controller.py +0 -8
  10. tecnicas/controllers/views_controller/api_rating_controller.py +6 -53
  11. tecnicas/controllers/views_controller/create_session/panel_basic_controller.py +50 -8
  12. tecnicas/controllers/views_controller/create_session/panel_codes_controller.py +24 -22
  13. tecnicas/controllers/views_controller/create_session/panel_create_controller.py +404 -232
  14. tecnicas/controllers/views_controller/create_session/panel_words_controller.py +36 -7
  15. tecnicas/controllers/views_controller/session_management/details_escala_controller.py +39 -14
  16. tecnicas/controllers/views_controller/session_management/monitor_controller.py +53 -4
  17. tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py +18 -62
  18. tecnicas/controllers/views_controller/session_management/monitor_rata_controller.py +46 -0
  19. tecnicas/controllers/views_controller/sessions_tester/convencional_scales_controller.py +158 -0
  20. tecnicas/controllers/views_controller/{main_tester_form_controller.py → sessions_tester/init_session_tester_controller.py} +103 -23
  21. tecnicas/controllers/views_controller/{list_sessions_tester_controller.py → sessions_tester/list_sessions_tester_controller.py} +0 -0
  22. tecnicas/controllers/views_controller/{login_tester_controller.py → sessions_tester/login_session_tester_controller.py} +24 -24
  23. tecnicas/controllers/views_controller/vocabulary_manage/create_vocabulary_controller.py +110 -0
  24. tecnicas/controllers/views_controller/vocabulary_manage/list_vocabulary_controller.py +48 -0
  25. tecnicas/forms/__init__.py +6 -3
  26. tecnicas/forms/create_session/sesiob_basic_cata_form.py +27 -0
  27. tecnicas/forms/{sesion_basic_form.py → create_session/sesion_basic_form.py} +1 -3
  28. tecnicas/forms/{sesion_tags_form.py → create_session/sesion_tags_form.py} +1 -1
  29. tecnicas/forms/vocabulary_select.py +15 -0
  30. tecnicas/migrations/0021_rename_nomre_vocabulario_vocabulario_nombre_vocabulario_and_more.py +30 -0
  31. tecnicas/models/calificacion.py +4 -1
  32. tecnicas/models/dato.py +1 -1
  33. tecnicas/models/dato_valor.py +2 -2
  34. tecnicas/models/vocabulario.py +7 -4
  35. tecnicas/static/img/letters.webp +0 -0
  36. tecnicas/static/js/choose-vocabulary.js +67 -0
  37. tecnicas/static/js/created-scale.js +2 -0
  38. tecnicas/static/js/created-vocabulary.js +48 -0
  39. tecnicas/static/js/download-table-csv.js +78 -0
  40. tecnicas/static/js/panel-basic-cata.js +31 -0
  41. tecnicas/static/js/panel-words.js +1 -1
  42. tecnicas/templates/tecnicas/components/form-scale-continue.html +62 -0
  43. tecnicas/templates/tecnicas/components/form-scale-structure.html +52 -0
  44. tecnicas/templates/tecnicas/components/item_session_tester.html +1 -1
  45. tecnicas/templates/tecnicas/components/item_vocabulary.html +20 -0
  46. tecnicas/templates/tecnicas/components/table-convencional.html +29 -23
  47. tecnicas/templates/tecnicas/create_sesion/conf-panel-vocabulary.html +62 -0
  48. tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html +7 -3
  49. tecnicas/templates/tecnicas/create_sesion/panel-basic-cata.html +85 -0
  50. tecnicas/templates/tecnicas/forms_tester/convencional.html +19 -128
cata_system/settings.py CHANGED
@@ -25,7 +25,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
25
  # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
26
 
27
  # SECURITY WARNING: keep the secret key used in production secret!
28
- SECRET_KEY = 'django-insecure-u)t2290d+c@$$7@!%@m&=w44$a@haxew8$!!tggh!up-+0-ll('
29
 
30
  # SECURITY WARNING: don't run with debug turned on in production!
31
  DEBUG = True
 
25
  # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
26
 
27
  # SECURITY WARNING: keep the secret key used in production secret!
28
+ SECRET_KEY = os.getenv("DJANGO_SECRET_KEY")
29
 
30
  # SECURITY WARNING: don't run with debug turned on in production!
31
  DEBUG = True
docker-compose.yml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ services: # Definicion de servicios. Se convertiran en contendores [Requerido]
2
+ app-django-cata-system: # Nombre del servicio para control de Docker Compose [Requerido]
3
+ hostname: django-cata-system # Nombre del host, [No requerido]
4
+ container_name: django-cata-system # Nombre del contenedor para Docker [No requerido]
5
+ build: .
6
+ ports: # Mapeo de puertos -> sintaxis= puerto del host físico (no debe estar ocupado al momento de crear el contenedor):puerto del contendor [No requerido, depende de si necesita conectividad al exterior]
7
+ - "9495:7860" # -"[host:]container[/protocol]"
tecnicas/admin.py CHANGED
@@ -6,7 +6,7 @@ from .models import Catador, Presentador
6
 
7
  from .models import Tecnica, SesionSensorial
8
 
9
- from .models import EsAtributo, Palabra
10
 
11
  from .models import Etiqueta, Escala, EtiquetasEscala
12
 
@@ -31,6 +31,7 @@ admin.site.register(SesionSensorial)
31
 
32
  admin.site.register(EsAtributo)
33
  admin.site.register(Palabra)
 
34
 
35
  admin.site.register(Escala)
36
  admin.site.register(EtiquetasEscala)
 
6
 
7
  from .models import Tecnica, SesionSensorial
8
 
9
+ from .models import EsAtributo, Palabra, Vocabulario
10
 
11
  from .models import Etiqueta, Escala, EtiquetasEscala
12
 
 
31
 
32
  admin.site.register(EsAtributo)
33
  admin.site.register(Palabra)
34
+ admin.site.register(Vocabulario)
35
 
36
  admin.site.register(Escala)
37
  admin.site.register(EtiquetasEscala)
tecnicas/controllers/__init__.py CHANGED
@@ -12,12 +12,6 @@ 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.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
@@ -28,4 +22,16 @@ from .views_controller.session_management.details_controller import DetallesCont
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
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  from .models_controller.particiapacion_controller import ParticipacionController
13
  from .models_controller.dato_controller import DatoController
14
 
 
 
 
 
 
 
15
  from .views_controller.create_session.panel_basic_controller import PanelBasicController
16
  from .views_controller.create_session.panel_tags_controller import PanelTagsController
17
  from .views_controller.create_session.panel_codes_controller import PanelCodesController
 
22
  from .views_controller.session_management.details_escala_controller import DetallesEscalasController
23
  from .views_controller.session_management.details_rata_controller import DetallesRATAController
24
  from .views_controller.session_management.monitor_controller import MonitorController
25
+ from .views_controller.session_management.monitor_escalas_controller import MonitorEscalasController
26
+ from .views_controller.session_management.monitor_rata_controller import MonitorRATAController
27
+
28
+ from .views_controller.sessions_tester.login_session_tester_controller import LoginSessionTesterController
29
+ from .views_controller.sessions_tester.init_session_tester_controller import InitSessionTesterController
30
+ from .views_controller.sessions_tester.convencional_scales_controller import ConvencionalScalesController
31
+ from .views_controller.sessions_tester.list_sessions_tester_controller import ListSessionsTesterController
32
+
33
+ from .views_controller.vocabulary_manage.create_vocabulary_controller import CreateVocabularyController
34
+ from .views_controller.vocabulary_manage.list_vocabulary_controller import ListVocabularyController
35
+
36
+ from .views_controller.api_rating_controller import ApiRatingController
37
+ from .views_controller.tester_list_controller import TesterListController
tecnicas/controllers/models_controller/calificacion_controller.py CHANGED
@@ -6,49 +6,22 @@ from tecnicas.utils import controller_error, getId
6
 
7
  class CalificacionController():
8
  def __init__(self, product: Producto | int, technique: Tecnica | int, tester: Catador | int):
 
 
 
9
  atributes = {
10
- "num_repeticion": 0,
11
- "id_tecnica_id": getId(technique),
12
  "id_producto_id": getId(product),
13
  "id_catador_id": getId(tester),
14
  }
15
 
16
- self.rating = Calificacion(**atributes)
17
-
18
- def validateRating(self):
19
- try:
20
- self.rating.clean()
21
- return self.rating
22
- except ValidationError as e:
23
- return controller_error("No es posible validar la calificación")
24
-
25
- def setRepetition(self, repetition: int = None) -> int | dict:
26
- try:
27
- if repetition is not None:
28
- self.rating.num_repeticion = repetition
29
- else:
30
- self.rating.num_repeticion = self.rating.id_tecnica.repeticion
31
-
32
- return self.rating.num_repeticion
33
- except ValidationError as e:
34
- return controller_error(e)
35
-
36
- def saveRating(self):
37
- try:
38
- self.rating.save()
39
- return self.rating
40
- except ValidationError as e:
41
- return controller_error(e)
42
 
43
  @staticmethod
44
  def getRatingsByTechnique(technique: Tecnica):
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
-
52
  return ratings
53
 
54
  @staticmethod
@@ -94,39 +67,53 @@ class CalificacionController():
94
  return ratings
95
 
96
  @staticmethod
97
- def checkProducsWithoutRating(
98
  positions: list[Posicion] = None,
99
- user_cata: str = None,
100
  repetition: int = None,
101
- technique: Tecnica = None,
102
- id_technique: int = None,
103
  num_words: int = None):
104
- check_products = [position.id_producto for position in positions]
 
 
 
 
105
 
106
  filters = {
107
- "user_tester": user_cata,
108
- "repetition": repetition
109
  }
110
 
111
- if technique is not None:
112
- filters["technique"] = technique
113
- elif id_technique is not None:
114
- filters["id_technique"] = id_technique
 
115
 
116
- ratings = CalificacionController.getRatings(**filters)
 
 
 
 
117
 
 
118
  if len(ratings) == 0:
119
- return positions
120
 
121
- ratings_dict = defaultdict(list)
 
122
 
123
- for rat in ratings:
124
- ratings_dict[rat.id_producto.id].append(rat)
125
 
126
- for index, product in enumerate(check_products):
127
- ratings_of_product = ratings_dict.get(product.id, [])
128
 
129
- if len(ratings_of_product) < num_words or len(ratings_of_product) == 0:
130
- return positions[index]
131
-
132
- return controller_error("Sin productos por calificar")
 
 
 
 
 
6
 
7
  class CalificacionController():
8
  def __init__(self, product: Producto | int, technique: Tecnica | int, tester: Catador | int):
9
+ technique = Tecnica.objects.only(
10
+ "repeticion").get(id=getId(technique))
11
+
12
  atributes = {
13
+ "num_repeticion": technique.repeticion,
14
+ "id_tecnica": technique,
15
  "id_producto_id": getId(product),
16
  "id_catador_id": getId(tester),
17
  }
18
 
19
+ (self.rating, created) = Calificacion.objects.get_or_create(**atributes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  @staticmethod
22
  def getRatingsByTechnique(technique: Tecnica):
23
  repetition = technique.repeticion
 
 
 
 
24
  ratings = list(Calificacion.objects.filter(id_tecnica=technique))
 
25
  return ratings
26
 
27
  @staticmethod
 
67
  return ratings
68
 
69
  @staticmethod
70
+ def checkPositionWithoutRating(
71
  positions: list[Posicion] = None,
72
+ user_cata: Catador | str = None,
73
  repetition: int = None,
74
+ technique: Tecnica | int = None,
 
75
  num_words: int = None):
76
+ end_rating = False
77
+
78
+ # Obtener lista con codigos de productos
79
+ check_products = [
80
+ position.id_producto.codigoProducto for position in positions]
81
 
82
  filters = {
83
+ "num_repeticion": repetition,
84
+ "id_tecnica": getId(technique)
85
  }
86
 
87
+ if isinstance(user_cata, Catador):
88
+ filters["id_catador"] = user_cata
89
+ else:
90
+ filters["id_catador"] = Catador.objects.get(
91
+ user__username=user_cata)
92
 
93
+ ratings = list(Calificacion.objects.filter(**filters).select_related(
94
+ "id_producto",
95
+ "id_tecnica",
96
+ "id_catador",
97
+ ))
98
 
99
+ # Si no hay calificaciones regresar las posiciones
100
  if len(ratings) == 0:
101
+ return (positions, end_rating)
102
 
103
+ rating_products = [
104
+ rating.id_producto.codigoProducto for rating in ratings]
105
 
106
+ for index, rating in enumerate(ratings):
107
+ data_rating = rating.dato_calificacion.all()
108
 
109
+ if len(data_rating) < num_words or len(data_rating) == 0:
110
+ return (positions[index], end_rating)
111
 
112
+ next_product = list(set(check_products) - set(rating_products))
113
+ if len(next_product) != 0:
114
+ next_position = [
115
+ position for position in positions if position.id_producto.codigoProducto == next_product[0]][0]
116
+ return (next_position, end_rating)
117
+ else:
118
+ end_rating = True
119
+ return (positions[-1], end_rating)
tecnicas/controllers/models_controller/dato_controller.py CHANGED
@@ -12,7 +12,11 @@ class DatoController():
12
  }
13
 
14
  self.data = Dato(**atributes)
15
- self.value_data = ValorDecimal(valor=value_rating)
 
 
 
 
16
 
17
  def setRating(self, new_rating: Calificacion):
18
  try:
@@ -35,19 +39,14 @@ class DatoController():
35
  except ValidationError as e:
36
  return controller_error(e.message)
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,
44
- valor=self.value_data.valor
45
- )
46
  else:
47
- self.value_data = ValorDecimal(
48
- id_dato=self.data,
49
- valor=self.value_data.valor
50
- )
51
 
52
  return self.value_data
53
 
@@ -88,7 +87,7 @@ class DatoController():
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__user__username"),
93
  dato_valor=F("valor")
94
  )
 
12
  }
13
 
14
  self.data = Dato(**atributes)
15
+
16
+ if isinstance(value_rating, bool):
17
+ self.value_data = ValorBooleano(valor=value_rating)
18
+ else:
19
+ self.value_data = ValorDecimal(valor=value_rating)
20
 
21
  def setRating(self, new_rating: Calificacion):
22
  try:
 
39
  except ValidationError as e:
40
  return controller_error(e.message)
41
 
42
+ def setValue(self, new_value=None):
43
+ if new_value:
44
+ if isinstance(new_value, bool):
45
+ self.value_data = ValorBooleano(valor=new_value)
46
+ else:
47
+ self.value_data = ValorDecimal(valor=new_value)
 
 
48
  else:
49
+ self.value_data.id_dato = self.data
 
 
 
50
 
51
  return self.value_data
52
 
 
87
  repeticion=F("id_dato__id_calificacion__num_repeticion"),
88
  producto_code=F(
89
  "id_dato__id_calificacion__id_producto__codigoProducto"),
90
+ usuario_catador=F(
91
  "id_dato__id_calificacion__id_catador__user__username"),
92
  dato_valor=F("valor")
93
  )
tecnicas/controllers/models_controller/palabras_controller.py CHANGED
@@ -22,17 +22,16 @@ class PalabrasController():
22
  @staticmethod
23
  def getWordsInTechnique(technique: Tecnica):
24
  if technique.id_estilo.nombre_estilo == "atributos":
25
- es_atribute = EsAtributo.objects.get(id_tecnica=technique)
26
- words = list(es_atribute.palabras.all())
 
27
  return words
28
  elif technique.id_estilo.nombre_estilo == "vocabulario":
29
- try:
30
- palabras = Palabra.objects.filter(
31
- vocabulario__esvocabulario__id_tecnica=technique
32
- )
33
- return list(palabras.distinct()) if palabras.exists() else controller_error("Técnica sin palabras con vocabulario")
34
- except Exception as e:
35
  return controller_error("Técnica sin palabras con vocabulario")
 
36
 
37
  @staticmethod
38
  def getWordsWithoutData(recoreded_data: list[Dato], words: list[Palabra]):
 
22
  @staticmethod
23
  def getWordsInTechnique(technique: Tecnica):
24
  if technique.id_estilo.nombre_estilo == "atributos":
25
+ words = list(technique.tecnica_esatributo.palabras.all())
26
+ if not words:
27
+ return controller_error("Técnica sin palabras")
28
  return words
29
  elif technique.id_estilo.nombre_estilo == "vocabulario":
30
+ words = list(
31
+ technique.tecnica_esvacabulario.id_vocabulario.palabras.all())
32
+ if not words:
 
 
 
33
  return controller_error("Técnica sin palabras con vocabulario")
34
+ return words
35
 
36
  @staticmethod
37
  def getWordsWithoutData(recoreded_data: list[Dato], words: list[Palabra]):
tecnicas/controllers/models_controller/particiapacion_controller.py CHANGED
@@ -16,15 +16,12 @@ class ParticipacionController():
16
  return controller_error("No se ha encontrado la participación")
17
 
18
  @staticmethod
19
- def finishSession(id_participation: int):
20
- try:
21
- participation = Participacion.objects.get(id=id_participation)
22
- participation.finalizado = True
23
- participation.activo = False
24
- participation.save()
25
- return participation
26
- except Participacion.DoesNotExist:
27
- return controller_error("No se ha encontrado la participación")
28
 
29
  @staticmethod
30
  def outSession(tester: Catador, session: SesionSensorial):
 
16
  return controller_error("No se ha encontrado la participación")
17
 
18
  @staticmethod
19
+ def finishSession(participation: Participacion):
20
+ participation.refresh_from_db()
21
+ participation.finalizado = True
22
+ participation.activo = False
23
+ participation.save()
24
+ return participation
 
 
 
25
 
26
  @staticmethod
27
  def outSession(tester: Catador, session: SesionSensorial):
tecnicas/controllers/models_controller/sesion_controller.py CHANGED
@@ -131,14 +131,6 @@ class SesionController():
131
  else:
132
  use_session = session
133
 
134
- (is_update_participations,
135
- message) = ParticipacionController.outAllInSession(use_session)
136
-
137
- if not is_update_participations:
138
- return controller_error(message)
139
-
140
  use_session.activo = False
141
-
142
  use_session.save()
143
-
144
  return session
 
131
  else:
132
  use_session = session
133
 
 
 
 
 
 
 
134
  use_session.activo = False
 
135
  use_session.save()
 
136
  return session
tecnicas/controllers/views_controller/api_rating_controller.py CHANGED
@@ -7,65 +7,18 @@ class ApiRatingController():
7
  self.rating_controller = rating_controller
8
  self.data_controller = data_controller
9
 
10
- def setRating(self) -> int | dict:
11
- repetition = self.rating_controller.setRepetition()
12
- if isinstance(repetition, dict):
13
- return controller_error(repetition["error"])
14
- return repetition
15
 
16
- def saveRating(self):
17
- rating = self.rating_controller.saveRating()
18
- if isinstance(rating, dict):
19
- return controller_error(rating["error"])
20
- return rating
21
-
22
- def setRatingInData(self):
23
- update_rating = self.data_controller.setRating(
24
- self.rating_controller.rating)
25
- if isinstance(update_rating, dict):
26
- return controller_error(update_rating["error"])
27
- return update_rating
28
-
29
- def saveData(self):
30
  data = self.data_controller.saveData()
31
- if isinstance(data, dict):
32
- return controller_error(data["error"])
33
- return data
34
-
35
- def setValueRating(self):
36
- self.data_controller.setInstanceValue()
37
-
38
- def saveValue(self):
39
- value = self.data_controller.saveValue()
40
- if isinstance(value, dict):
41
- return controller_error(value["error"])
42
-
43
- def logicView(self) -> dict:
44
- validate = self.rating_controller.validateRating()
45
- if isinstance(validate, dict):
46
- return controller_error(validate["error"])
47
-
48
- reptition = self.setRating()
49
- if isinstance(reptition, dict):
50
- return controller_error(reptition["error"])
51
-
52
- rating = self.saveRating()
53
- if isinstance(rating, dict):
54
- return controller_error(rating["error"])
55
-
56
- rating_data = self.setRatingInData()
57
- if isinstance(rating_data, dict):
58
- return controller_error(rating_data["error"])
59
-
60
- data = self.saveData()
61
  if isinstance(data, dict):
62
  return controller_error(data["error"])
63
 
64
- value = self.setValueRating()
65
- if isinstance(value, dict):
66
- return controller_error(value["error"])
67
 
68
- value_save = self.saveValue()
69
  if isinstance(value_save, dict):
70
  return controller_error(value_save["error"])
71
 
 
7
  self.rating_controller = rating_controller
8
  self.data_controller = data_controller
9
 
10
+ def controllPostScales(self) -> dict:
11
+ self.data_controller.setRating(
12
+ new_rating=self.rating_controller.rating
13
+ )
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  data = self.data_controller.saveData()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  if isinstance(data, dict):
17
  return controller_error(data["error"])
18
 
19
+ self.data_controller.setValue()
 
 
20
 
21
+ value_save = self.data_controller.saveValue()
22
  if isinstance(value_save, dict):
23
  return controller_error(value_save["error"])
24
 
tecnicas/controllers/views_controller/create_session/panel_basic_controller.py CHANGED
@@ -1,4 +1,4 @@
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
@@ -10,6 +10,14 @@ class PanelBasicController():
10
  "numero_repeticiones": 1
11
  }
12
 
 
 
 
 
 
 
 
 
13
  def __init__(self):
14
  pass
15
 
@@ -23,7 +31,7 @@ class PanelBasicController():
23
  }
24
 
25
  response = render(
26
- request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
27
  return response
28
 
29
  @staticmethod
@@ -44,11 +52,11 @@ class PanelBasicController():
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
 
@@ -63,7 +71,7 @@ class PanelBasicController():
63
  }
64
 
65
  response = render(
66
- request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
67
  return response
68
 
69
  @staticmethod
@@ -88,7 +96,7 @@ class PanelBasicController():
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
@@ -96,10 +104,44 @@ class PanelBasicController():
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
 
1
+ from tecnicas.forms import SesionBasicForm, SesionBasicCATAForm
2
  from django.http import HttpRequest
3
  from django.shortcuts import redirect, render
4
  from django.urls import reverse
 
10
  "numero_repeticiones": 1
11
  }
12
 
13
+ url_panel_basic = "tecnicas/create_sesion/configuracion-panel-basic.html"
14
+ url_panel_basic_cata = "tecnicas/create_sesion/panel-basic-cata.html"
15
+
16
+ url_next_panel_scales = "cata_system:panel_configuracion_tags"
17
+ url_next_panel_cata = "cata_system:panel_configuracion_codes"
18
+
19
+ url_select_technique = "cata_system:seleccion_tecnica"
20
+
21
  def __init__(self):
22
  pass
23
 
 
31
  }
32
 
33
  response = render(
34
+ request, PanelBasicController.url_panel_basic, view_context)
35
  return response
36
 
37
  @staticmethod
 
52
  response = redirect(
53
  reverse("cata_system:panel_configuracion_tags"))
54
  else:
55
+ response = render(request, PanelBasicController.url_panel_basic, {
56
  "form_sesion": form, "error": "Información no valida"})
57
  except KeyError:
58
  response = redirect(reverse(
59
+ PanelBasicController.url_select_technique) + "?error=error en datos de configuracion")
60
 
61
  return response
62
 
 
71
  }
72
 
73
  response = render(
74
+ request, PanelBasicController.url_panel_basic, view_context)
75
  return response
76
 
77
  @staticmethod
 
96
  key, f"Valor inválido para '{key}': se esperaba {expected}, se recibió {actual}")
97
 
98
  if form.errors:
99
+ response = render(request, PanelBasicController.url_panel_basic, {
100
  "form_sesion": form, "error": "No puedes modificar el número de catadores o repeticiones"})
101
  else:
102
  values["name_tecnica"] = name_tecnica
 
104
  response = redirect(
105
  reverse("cata_system:panel_configuracion_tags"))
106
  else:
107
+ response = render(request, PanelBasicController.url_panel_basic, {
108
  "form_sesion": form, "error": "Información no valida"})
109
  except KeyError:
110
  response = redirect(reverse(
111
+ PanelBasicController.url_select_technique) + "?error=error en datos de configuracion")
112
+
113
+ return response
114
+
115
+ @staticmethod
116
+ def controllGetCATA(request: HttpRequest):
117
+ form_sesion = SesionBasicCATAForm()
118
+
119
+ view_context = {
120
+ "form_sesion": form_sesion,
121
+ "use_technique": "cata"
122
+ }
123
+
124
+ return render(
125
+ request, PanelBasicController.url_panel_basic_cata, view_context)
126
+
127
+ @staticmethod
128
+ def controllPostCATA(request: HttpRequest, name_tecnica: str):
129
+ form = SesionBasicCATAForm(request.POST)
130
+
131
+ if form.is_valid():
132
+ values = {}
133
+ for name, value in form.cleaned_data.items():
134
+ if name == "estilo_palabras":
135
+ values[name] = value.id
136
+ else:
137
+ values[name] = value
138
+
139
+ values["name_tecnica"] = name_tecnica
140
+ request.session['form_basic'] = values
141
+ response = redirect(
142
+ reverse(PanelBasicController.url_next_panel_cata))
143
+ else:
144
+ response = render(request, PanelBasicController.url_panel_basic, {
145
+ "form_sesion": form, "error": "Información no valida"})
146
 
147
  return response
tecnicas/controllers/views_controller/create_session/panel_codes_controller.py CHANGED
@@ -7,15 +7,16 @@ 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
 
@@ -27,14 +28,11 @@ class PanelCodesController():
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 = []
@@ -60,11 +58,11 @@ class PanelCodesController():
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):
@@ -78,10 +76,10 @@ class PanelCodesController():
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
 
@@ -93,22 +91,26 @@ class PanelCodesController():
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
- )
 
 
 
 
 
7
 
8
 
9
  class PanelCodesController():
10
+ url_current_panel = "tecnicas/create_sesion/configuracion-panel-codes.html"
11
+ url_next_panel = "cata_system:panel_configuracion_words"
12
+
13
  def __init__(self):
14
  pass
15
 
16
  @staticmethod
17
  def controllGetEscalas(request: HttpRequest, data):
18
+ num_products = data["numero_productos"]
19
+ num_tester = data["numero_catadores"]
 
 
20
 
21
  codes_products = generarCodigos(num_products)
22
 
 
28
  "use_technique": "escalas"
29
  }
30
 
31
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
32
 
33
  @staticmethod
34
  def controllPostEscalas(request: HttpRequest, data):
35
+ num_tester = data["numero_catadores"]
 
 
 
36
 
37
  sorts_code = json.loads(request.POST.get("sort_codes"))
38
  codes = []
 
58
 
59
  codes_sort["sort_codes"] = sorts_code
60
  request.session["form_codes"] = codes_sort
61
+ return redirect(reverse(PanelCodesController.url_next_panel))
62
  else:
63
  context_codes_form["error"] = "error en los datos recibidos"
64
 
65
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
66
 
67
  @staticmethod
68
  def controllGetRATA(request: HttpRequest, data):
 
76
  "use_technique": "rata"
77
  }
78
 
79
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
80
 
81
  @staticmethod
82
+ def controllPostRATA(request: HttpRequest, is_rata: True):
83
  codes = []
84
  context_codes_form = {}
85
 
 
91
 
92
  context_codes_form = {
93
  "form_codes": form_codes,
94
+ "use_technique": "rata" if is_rata else "cata"
95
  }
96
 
97
  if form_codes.is_valid():
98
  request.session["form_codes"] = codes
99
+ return redirect(reverse(PanelCodesController.url_next_panel))
100
  else:
101
  context_codes_form["error"] = "error en los datos recibidos"
102
 
103
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
104
 
105
  @staticmethod
106
+ def controllGetCATA(request: HttpRequest, data):
107
  num_products = data["numero_productos"]
108
+ codes_products = generarCodigos(num_products)
109
+ form_codes = CodesForm(codes=codes_products)
110
+
111
+ context_codes_form = {
112
+ "form_codes": form_codes,
113
+ "use_technique": "cata"
114
+ }
115
+
116
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
tecnicas/controllers/views_controller/create_session/panel_create_controller.py CHANGED
@@ -2,6 +2,7 @@ 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
 
@@ -21,139 +22,155 @@ class PanelCreateController():
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
 
@@ -163,112 +180,267 @@ class PanelCreateController():
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")
 
2
  from django.db import transaction
3
  from django.shortcuts import render
4
  from tecnicas.utils import general_error
5
+ from tecnicas.models import EsAtributo, EsVocabulario, Vocabulario, Tecnica, TipoTecnica, EstiloPalabra, Producto, Palabra, SesionSensorial
6
  from tecnicas.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, EstiloPalabrasController, PalabrasController, SesionController
7
  from tecnicas.utils import deleteDataSession
8
 
 
22
  if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
23
  deleteDataSession(request)
24
  return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
25
+ try:
26
+ with transaction.atomic():
27
+ # ////////////////////////////////////////////////////// #
28
+ #
29
+ # First step: Create technique and scale with their tags #
30
+ #
31
+ # ////////////////////////////////////////////////////// #
32
+ data_basic = request.session["form_basic"]
33
+ controllerTechnique = TecnicaController()
34
+ controllerTechnique.setTechniqueFromBasicData(
35
+ basic=data_basic)
36
+ technique = controllerTechnique.saveTechnique()
37
+ if not technique:
38
+ raise ValueError("Error al guardar la técnica")
39
+
40
+ data_scale = {
41
+ "id_scale": data_basic["tipo_escala"],
42
+ "size": data_basic["tamano_escala"],
43
+ "technique": technique
44
+ }
45
 
46
+ controllerScale = EscalaController(data=data_scale)
47
+
48
+ scale = controllerScale.saveScale()
49
+ if isinstance(scale, dict):
50
+ raise ValueError(scale["error"])
51
+
52
+ dict_tags = request.session["form_tags"]
53
+ saved_related_tags = controllerScale.realteTags(dict_tags)
54
+ if "error" in saved_related_tags:
55
+ raise ValueError(saved_related_tags["error"])
56
+
57
+ # ////////////////////////////////////////////////////////// #
58
+ #
59
+ # Second step: Create orders, productos and set the position #
60
+ #
61
+ # ////////////////////////////////////////////////////////// #
62
+ data_codes = request.session["form_codes"]
63
+
64
+ list_codes_dict = data_codes["product_codes"]
65
+
66
+ codes = []
67
+ for product in list_codes_dict:
68
+ code = next(iter(product.values()))
69
+ codes.append(code)
70
+
71
+ controllerProducts = ProductosController(
72
+ codes=codes,
73
+ technique=technique
74
+ )
75
+
76
+ controllerProducts.setProductsNoSave()
77
+ saved_prodcuts = controllerProducts.saveProducts()
78
+ if isinstance(saved_prodcuts, dict):
79
+ raise ValueError(saved_prodcuts["error"])
80
+
81
+ raw_sort_codes = data_codes["sort_codes"]
82
+ controllerOrdes = OrdenesController(
83
+ raw_orders=raw_sort_codes,
84
+ list_products=saved_prodcuts,
85
+ technique=technique
86
+ )
87
+
88
+ controllerOrdes.setOrdersToSave()
89
+ saved_orders = controllerOrdes.saveOrders()
90
+ if isinstance(saved_orders, dict):
91
+ raise ValueError(saved_orders["error"])
92
+
93
+ seded_positions = controllerOrdes.setPositions()
94
+ if isinstance(seded_positions, dict):
95
+ raise ValueError(seded_positions["error"])
96
+
97
+ saved_postions = controllerOrdes.savePositions()
98
+ if isinstance(saved_postions, dict):
99
+ raise ValueError(saved_prodcuts["error"])
100
+
101
+ # /////////////////////////////////////////////////////// #
102
+ #
103
+ # Third step: Create relations technique with Words Style #
104
+ #
105
+ # /////////////////////////////////////////////////////// #
106
+ style_words = technique.id_estilo.nombre_estilo
107
+ if style_words == "atributos":
108
+ ids_words = request.session["form_words"]
109
+ words_controller = PalabrasController(ids=ids_words)
110
+
111
+ words_to_use = words_controller.setWords()
112
+ if isinstance(words_to_use, dict):
113
+ raise ValueError(words_to_use["error"])
114
+
115
+ style_controller = EstiloPalabrasController(
116
+ technique=technique, words=words_to_use)
117
+
118
+ instace_style = style_controller.createAndSaveInstaceStyle()
119
+ if isinstance(instace_style, dict):
120
+ raise ValueError(instace_style["error"])
121
+
122
+ words_using = style_controller.relatedWords()
123
+ if isinstance(words_using, dict):
124
+ raise ValueError(words_using["error"])
125
+ elif style_words == "vocabulario":
126
+ name_vocabulary = request.session["form_words"]
127
+ vocabulary = Vocabulario.objects.get(
128
+ nombre_vocabulario=name_vocabulary)
129
+
130
+ es_vocabulary = EsVocabulario.objects.create(
131
+ id_tecnica=technique,
132
+ id_vocabulario=vocabulary
133
+ )
134
+ else:
135
+ raise ValueError("Estilo de palabas no permitido")
136
+
137
+ # //////////////////////////////////////////////////////// #
138
+ #
139
+ # Fourth step: Create session and relat with the technique #
140
+ #
141
+ # //////////////////////////////////////////////////////// #
142
+ session_controller = SesionController(
143
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
144
+ technique=technique,
145
+ creator=request.user.user_presentador
146
+ )
147
+
148
+ setting_session = session_controller.setSession()
149
+ if isinstance(setting_session, dict):
150
+ raise ValueError(setting_session["error"])
151
+
152
+ saved_session = session_controller.saveSession()
153
+ if isinstance(saved_session, dict):
154
+ raise ValueError(saved_session["error"])
155
+
156
+ context = {
157
+ "message": "sesión creada",
158
+ "data": {
159
+ "codigo_sesion": saved_session.codigo_sesion,
160
+ "nombre_sesion": saved_session.nombre_sesion
161
+ }
 
 
 
 
 
162
  }
 
163
 
164
+ # ////////////////////////////////// #
165
+ #
166
+ # Final step: Delete date in session #
167
+ #
168
+ # ////////////////////////////////// #
169
 
170
+ deleteDataSession(request)
171
+ return JsonResponse(context)
172
+ except ValueError as e:
173
+ return general_error(f"Error: {e}")
174
  else:
175
  return general_error("No se ha establecido acción")
176
 
 
180
  if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
181
  deleteDataSession(request)
182
  return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
183
+ try:
184
+ with transaction.atomic():
185
+ # ////////////////////////////////////////////////////// #
186
+ #
187
+ # First step: Create technique and scale with their tags #
188
+ #
189
+ # ////////////////////////////////////////////////////// #
190
+ data_basic = request.session["form_basic"]
191
+ data_basic["numero_catadores"] = 0
192
+ data_basic["numero_repeticiones"] = 1
193
+
194
+ technique = Tecnica.objects.create(
195
+ tipo_tecnica=TipoTecnica.objects.get(
196
+ nombre_tecnica=data_basic["name_tecnica"]),
197
+ id_estilo=EstiloPalabra.objects.get(
198
+ id=data_basic["estilo_palabras"]),
199
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
200
+ limite_catadores=data_basic["numero_catadores"],
201
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
202
+ )
203
+
204
+ if not technique:
205
+ raise ValueError("Error al guardar la técnica")
206
+
207
+ data_scale = {
208
+ "id_scale": data_basic["tipo_escala"],
209
+ "size": data_basic["tamano_escala"],
210
+ "technique": technique
211
+ }
212
 
213
+ controllerScale = EscalaController(data=data_scale)
214
+
215
+ scale = controllerScale.saveScale()
216
+ if isinstance(scale, dict):
217
+ raise ValueError(scale["error"])
218
+
219
+ dict_tags = request.session["form_tags"]
220
+ saved_related_tags = controllerScale.realteTags(dict_tags)
221
+ if "error" in saved_related_tags:
222
+ raise ValueError(saved_related_tags["error"])
223
+
224
+ # ////////////////////////////////////////////// #
225
+ #
226
+ # Second step: Create productos with their codes #
227
+ #
228
+ # ////////////////////////////////////////////// #
229
+ codes = request.session["form_codes"]
230
+
231
+ if not codes:
232
+ raise ValueError("No hay códigos de productos")
233
+
234
+ products_without_save = []
235
+ for code in codes:
236
+ product = Producto(
237
+ codigoProducto=code,
238
+ id_tecnica=technique
239
+ )
240
+ products_without_save.append(product)
241
+
242
+ Producto.objects.bulk_create(products_without_save)
243
+
244
+ # /////////////////////////////////////////////////////// #
245
+ #
246
+ # Third step: Create relations technique with Words Style #
247
+ #
248
+ # /////////////////////////////////////////////////////// #
249
+ style_words = technique.id_estilo.nombre_estilo
250
+
251
+ if style_words == "atributos":
252
+ raw_ids_words = request.session["form_words"]
253
+ ids_words = [int(id_w) for id_w in raw_ids_words]
254
+
255
+ words = Palabra.objects.filter(id__in=ids_words)
256
+
257
+ style_atribute = EsAtributo.objects.create(
258
+ id_tecnica=technique
259
+ )
260
+
261
+ if not style_atribute:
262
+ raise ValueError(
263
+ "Error al intentar relacionar las palabras con la técnica")
264
+
265
+ style_atribute.palabras.set(words)
266
+
267
+ elif style_words == "vocabulario":
268
+ name_vocabulary = request.session["form_words"]
269
+ try:
270
+ vocabulary = Vocabulario.objects.get(
271
+ nombre_vocabulario=name_vocabulary)
272
+ except Vocabulario.DoesNotExist:
273
+ raise ValueError("Vocabulario no encontrado")
274
+
275
+ es_vocabulary = EsVocabulario.objects.create(
276
+ id_tecnica=technique,
277
+ id_vocabulario=vocabulary
278
+ )
279
+ if not es_vocabulary:
280
+ raise ValueError(
281
+ "Error al intentar relacionar el vocabulario con la técnica")
282
+
283
+ else:
284
+ raise ValueError("Estilo de palabas no permitido")
285
+
286
+ # //////////////////////////////////////////////////////// #
287
+ #
288
+ # Fourth step: Create session and relat with the technique #
289
+ #
290
+ # //////////////////////////////////////////////////////// #
291
+ session = SesionSensorial.objects.create(
292
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
293
+ technique=technique,
294
+ creator=request.user.user_presentador
295
+ )
296
+
297
+ if not session:
298
+ raise ValueError("Error al crear sesion sensorial")
299
+
300
+ context = {
301
+ "message": "sesión creada",
302
+ "data": {
303
+ "codigo_sesion": session.codigo_sesion,
304
+ "nombre_sesion": session.nombre_sesion
305
+ }
 
 
 
306
  }
 
307
 
308
+ # ////////////////////////////////// #
309
+ #
310
+ # Final step: Delete date en session #
311
+ #
312
+ # ////////////////////////////////// #
313
+
314
+ deleteDataSession(request)
315
+ return JsonResponse(context)
316
+ except ValueError as e:
317
+ return general_error(f"Error: {e}")
318
+ else:
319
+ return general_error("No se ha establecido acción")
320
+
321
+ @staticmethod
322
+ def controllPostCATA(request: HttpRequest):
323
+ if request.POST.get('action') == 'create_session':
324
+ if not request.session.get("form_codes") or not request.session.get("form_words"):
325
  deleteDataSession(request)
326
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
327
+ try:
328
+ with transaction.atomic():
329
+ # //////////////////////////// #
330
+ #
331
+ # First step: Create technique #
332
+ #
333
+ # //////////////////////////// #
334
+ data_basic = request.session["form_basic"]
335
+ data_basic["numero_catadores"] = 0
336
+ data_basic["numero_repeticiones"] = 1
337
+
338
+ technique = Tecnica.objects.create(
339
+ tipo_tecnica=TipoTecnica.objects.get(
340
+ nombre_tecnica=data_basic["name_tecnica"]),
341
+ id_estilo=EstiloPalabra.objects.get(
342
+ id=data_basic["estilo_palabras"]),
343
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
344
+ limite_catadores=data_basic["numero_catadores"],
345
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
346
+ )
347
+
348
+ if not technique:
349
+ raise ValueError("Error al guardar la técnica")
350
+
351
+ # ////////////////////////////////////////////// #
352
+ #
353
+ # Second step: Create productos with their codes #
354
+ #
355
+ # ////////////////////////////////////////////// #
356
+ codes = request.session["form_codes"]
357
+
358
+ if not codes:
359
+ raise ValueError("No hay códigos de productos")
360
+
361
+ products_without_save = []
362
+ for code in codes:
363
+ product = Producto(
364
+ codigoProducto=code,
365
+ id_tecnica=technique
366
+ )
367
+ products_without_save.append(product)
368
+
369
+ Producto.objects.bulk_create(products_without_save)
370
+
371
+ # /////////////////////////////////////////////////////// #
372
+ #
373
+ # Third step: Create relations technique with Words Style #
374
+ #
375
+ # /////////////////////////////////////////////////////// #
376
+ style_words = technique.id_estilo.nombre_estilo
377
+
378
+ if style_words == "atributos":
379
+ raw_ids_words = request.session["form_words"]
380
+ ids_words = [int(id_w) for id_w in raw_ids_words]
381
+
382
+ words = Palabra.objects.filter(id__in=ids_words)
383
+
384
+ style_atribute = EsAtributo.objects.create(
385
+ id_tecnica=technique
386
+ )
387
+
388
+ if not style_atribute:
389
+ raise ValueError(
390
+ "Error al intentar relacionar las palabras con la técnica")
391
+
392
+ style_atribute.palabras.set(words)
393
+
394
+ elif style_words == "vocabulario":
395
+ name_vocabulary = request.session["form_words"]
396
+ try:
397
+ vocabulary = Vocabulario.objects.get(
398
+ nombre_vocabulario=name_vocabulary)
399
+ except Vocabulario.DoesNotExist:
400
+ raise ValueError("Vocabulario no encontrado")
401
+
402
+ es_vocabulary = EsVocabulario.objects.create(
403
+ id_tecnica=technique,
404
+ id_vocabulario=vocabulary
405
+ )
406
+ if not es_vocabulary:
407
+ raise ValueError(
408
+ "Error al intentar relacionar el vocabulario con la técnica")
409
+
410
+ else:
411
+ raise ValueError("Estilo de palabas no permitido")
412
+
413
+ # //////////////////////////////////////////////////////// #
414
+ #
415
+ # Fourth step: Create session and relat with the technique #
416
+ #
417
+ # //////////////////////////////////////////////////////// #
418
+ session = SesionSensorial.objects.create(
419
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
420
+ tecnica=technique,
421
+ creadoPor=request.user.user_presentador
422
+ )
423
+
424
+ if not session:
425
+ raise ValueError("Error al crear sesion sensorial")
426
+
427
+ context = {
428
+ "message": "sesión creada",
429
+ "data": {
430
+ "codigo_sesion": session.codigo_sesion,
431
+ "nombre_sesion": session.nombre_sesion
432
+ }
433
+ }
434
+
435
+ # ////////////////////////////////// #
436
+ #
437
+ # Final step: Delete date en session #
438
+ #
439
+ # ////////////////////////////////// #
440
+ deleteDataSession(request)
441
+ return JsonResponse(context)
442
+
443
+ except ValueError as e:
444
+ return general_error(f"Error: {e}")
445
  else:
446
  return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panel_words_controller.py CHANGED
@@ -1,5 +1,5 @@
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
@@ -7,27 +7,36 @@ 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
@@ -36,14 +45,34 @@ class PanelWordsController():
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"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.http import HttpRequest
2
+ from tecnicas.forms import WordForm, VocabularioSelectForm
3
  from django.shortcuts import render, redirect
4
  from django.urls import reverse
5
  from tecnicas.models import Palabra
 
7
 
8
 
9
  class PanelWordsController():
10
+ current_url_escalas_atribute = "tecnicas/create_sesion/configuracion-panel-words.html"
11
+ current_url_escalas_vocabulary = "tecnicas/create_sesion/conf-panel-vocabulary.html"
12
+
13
  def __init__(self):
14
  pass
15
 
16
  @staticmethod
17
+ def controllGetEscalasAtributes(request: HttpRequest):
18
  form = WordForm()
19
  context = {
20
  "form_word": form
21
  }
22
 
23
+ return render(request, PanelWordsController.current_url_escalas_atribute, context)
24
+
25
+ @staticmethod
26
+ def controllGetEscalasVocabulary(request: HttpRequest):
27
+ form = VocabularioSelectForm()
28
+ context = {"form": form}
29
+ return render(request, PanelWordsController.current_url_escalas_vocabulary, context)
30
 
31
  @staticmethod
32
+ def controllPostEscalasAtributes(request: HttpRequest):
33
  form = WordForm()
34
  context = {
35
  "form_word": form
36
  }
37
 
38
  if not request.POST.get("words"):
39
+ return render(request, PanelWordsController.current_url_escalas_atribute, context)
40
 
41
  words = json.loads(request.POST.get("words"))
42
  context["words"] = words
 
45
 
46
  if len(ids_words) != len(set(ids_words)):
47
  context["error"] = "existen palabras duplicadas"
48
+ return render(request, PanelWordsController.current_url_escalas_atribute, context)
49
 
50
  exist_words = Palabra.objects.filter(
51
  id__in=ids_words).count() == len(ids_words)
52
 
53
  if not exist_words:
54
  context["error"] = "algunas palabras no existen"
55
+ return render(request, PanelWordsController.current_url_escalas_atribute, context)
56
 
57
  request.session["form_words"] = ids_words
58
  return redirect(reverse("cata_system:creando_sesion"))
59
+
60
+ @staticmethod
61
+ def controllPostEscalasVocabulary(request: HttpRequest):
62
+ context = {}
63
+ if not request.POST.get("vocabulario"):
64
+ context["form"] = VocabularioSelectForm()
65
+ context["error"] = "No hay un vocabulario seleccionado"
66
+ return render(request, PanelWordsController.current_url_escalas_vocabulary, context)
67
+
68
+ form = VocabularioSelectForm(request.POST)
69
+ vocabulary: int
70
+ if form.is_valid():
71
+ vocabulary = form.cleaned_data["vocabulario"]
72
+ else:
73
+ context["form"] = VocabularioSelectForm()
74
+ context["error"] = "Erro al validar el vocabulario"
75
+ return render(request, PanelWordsController.current_url_escalas_vocabulary, context)
76
+
77
+ request.session["form_words"] = vocabulary.nombre_vocabulario
78
+ return redirect(reverse("cata_system:creando_sesion"))
tecnicas/controllers/views_controller/session_management/details_escala_controller.py CHANGED
@@ -14,8 +14,8 @@ Encabezados de como deben de aparecer los datos juntos
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
@@ -34,7 +34,7 @@ class DetallesEscalasController(DetallesController):
34
  context["error"] = error
35
  if message != "" or message:
36
  context["message"] = message
37
-
38
  return render(
39
  request, self.url_template, context)
40
 
@@ -43,28 +43,41 @@ class DetallesEscalasController(DetallesController):
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(
53
- technique=self.session.tecnica)
54
 
55
- if isinstance(ratings, dict) or not ratings:
56
  self.context["calificaciones"] = ratings_for_repetition
57
  self.context["existen_calificaciones"] = False
58
  return self.context
59
 
60
  data = DatoController.getWordValuesForConvecional(
61
- ratings=ratings, technique=self.session.tecnica)
62
 
63
  ratings_for_repetition = defaultdict(
64
  lambda: defaultdict(lambda: defaultdict(list)))
65
 
66
  for item in data:
67
- user = item["usuarioCatador"]
68
  rep = item["repeticion"]
69
  prod = item["producto_code"]
70
 
@@ -77,18 +90,30 @@ class DetallesEscalasController(DetallesController):
77
  ratings_for_repetition)
78
  self.context["existen_calificaciones"] = True
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
 
14
  from django.http import HttpRequest
15
  from django.shortcuts import render, redirect
16
  from django.urls import reverse
17
+ from tecnicas.models import SesionSensorial, Presentador, Participacion, Calificacion, Escala
18
+ from tecnicas.controllers import DatoController, PalabrasController, ParticipacionController
19
  from .details_controller import DetallesController
20
  from tecnicas.utils import defaultdict_to_dict, controller_error
21
  from collections import defaultdict
 
34
  context["error"] = error
35
  if message != "" or message:
36
  context["message"] = message
37
+
38
  return render(
39
  request, self.url_template, context)
40
 
 
43
  "use_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
44
  }
45
  self.context["sesion"] = self.session
46
+
47
+ technique = self.session.tecnica
48
+
49
+ # Datos de la escala usada
50
+ scale: Escala = technique.escala_tecnica
51
+
52
+ self.context["scale"] = {
53
+ "type": scale.id_tipo_escala.nombre_escala,
54
+ "size": scale.longitud
55
+ }
56
+
57
+ # Recuperar la palabras de la tecnica
58
  self.words = PalabrasController.getWordsInTechnique(
59
+ technique)
60
  self.context["palabras"] = [word.nombre_palabra for word in self.words]
61
 
62
+ # Se recuperan las calificaciones
63
  ratings_for_repetition = []
64
 
65
+ ratings = list(Calificacion.objects.filter(
66
+ id_tecnica=technique))
67
 
68
+ if not ratings:
69
  self.context["calificaciones"] = ratings_for_repetition
70
  self.context["existen_calificaciones"] = False
71
  return self.context
72
 
73
  data = DatoController.getWordValuesForConvecional(
74
+ ratings=ratings, technique=technique)
75
 
76
  ratings_for_repetition = defaultdict(
77
  lambda: defaultdict(lambda: defaultdict(list)))
78
 
79
  for item in data:
80
+ user = item["usuario_catador"]
81
  rep = item["repeticion"]
82
  prod = item["producto_code"]
83
 
 
90
  ratings_for_repetition)
91
  self.context["existen_calificaciones"] = True
92
 
93
+ # Se comprueba que ya no se pueda iniciar la repeticion
94
+ self.context["fin_repeticiones"] = technique.repeticion >= technique.repeticiones_max
95
+
96
  return self.context
97
 
98
+ def startRepetition(self, presenter: Presentador, request: HttpRequest):
99
  creator = presenter
100
  technique = self.session.tecnica
101
 
102
  if creator.user.username != self.session.creadoPor.user.username:
103
+ return self.getResponse(error="Solo el presentador que crea la sesión puede iniciar la repetición", request=request)
104
  elif self.session.activo:
105
+ return self.getResponse(error="La sesión ya está activada", request=request)
106
+ elif technique.repeticion >= technique.repeticiones_max:
107
+ return self.getResponse(error="Se ha alcanzado el número de repeticiones máxima", request=request)
108
+
109
+ there_participacions = Participacion.objects.filter(
110
+ tecnica=technique).exists()
111
+
112
+ if there_participacions:
113
+ (is_update_participations,
114
+ message) = ParticipacionController.outAllInSession(self.session)
115
+ if not is_update_participations:
116
+ return self.getResponse(error=message, request=request)
117
 
118
  self.session.activo = True
119
  technique.repeticion = technique.repeticion + 1
tecnicas/controllers/views_controller/session_management/monitor_controller.py CHANGED
@@ -1,10 +1,59 @@
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)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render
3
+ from tecnicas.models import SesionSensorial, Producto, EsAtributo, EsVocabulario
4
+ from tecnicas.controllers import ParticipacionController, SesionController
5
+ from tecnicas.utils import controller_error
6
 
7
 
8
  class MonitorController():
9
+ url_view: str
10
+ previus_view: str
11
+
12
  def __init__(self, session: SesionSensorial):
13
  self.sensorial_session = session
14
 
15
+ def setContext(self):
16
+ self.participations = ParticipacionController.getParticipationsInTechinique(
17
+ self.sensorial_session.tecnica)
18
+
19
+ self.context = {
20
+ "code_session": self.sensorial_session.codigo_sesion,
21
+ "session_name": self.sensorial_session.nombre_sesion,
22
+ "max_testers": self.sensorial_session.tecnica.limite_catadores,
23
+ "current_testers": len(self.participations),
24
+ "active_testers": len([part for part in self.participations if part.activo]),
25
+ "participations": self.participations,
26
+ "use_technique": self.sensorial_session.tecnica.tipo_tecnica.nombre_tecnica
27
+ }
28
+
29
+ def controlGetResponse(self, request: HttpRequest, error: str = "", message: str = ""):
30
+ self.setContext()
31
+
32
+ if error != "" or error:
33
+ self.context["error"] = error
34
+ if message != "" or message:
35
+ self.context["message"] = message
36
+
37
+ return render(request, self.url_view, self.context)
38
+
39
+ def getExpectedRatingsEscalasRapida(self):
40
+ num_products = Producto.objects.filter(
41
+ id_tecnica=self.sensorial_session.tecnica).count()
42
+ style_words = self.sensorial_session.tecnica.id_estilo
43
+ num_words: int
44
+
45
+ if style_words.nombre_estilo == "atributos":
46
+ num_words = EsAtributo.objects.get(
47
+ id_tecnica=self.sensorial_session.tecnica).palabras.count()
48
+ elif style_words.nombre_estilo == "vocabulario":
49
+ num_words = EsVocabulario.objects.get(
50
+ id_tecnica=self.sensorial_session.tecnica).id_vocabulario.palabras.count()
51
+
52
+ return num_products * num_words
53
+
54
+ def finishSession(self):
55
+ response = SesionController.finishRepetion(self.sensorial_session)
56
+ if isinstance(response, dict):
57
+ return controller_error(response["error"])
58
+ self.sensorial_session.refresh_from_db()
59
+ return self.sensorial_session
tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py CHANGED
@@ -1,93 +1,49 @@
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
 
73
- all_participations = ParticipacionController.getParticipationsInTechinique(
74
- technique=technique)
75
 
76
  if len(all_participations) < technique.limite_catadores:
77
  return (False, "No se ha alcanzado el número máximo de Catadores")
78
 
79
  for particiapation in all_participations:
80
- num_ratings_now = Calificacion.objects.filter(
81
- id_tecnica=technique, id_catador=particiapation.catador, num_repeticion=technique.repeticion).count()
 
 
 
82
 
83
  if num_ratings_now < expected_ratings_repetition:
84
  return (False, "No todos los catadores han finalizado su evaluación")
85
 
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
 
1
  from django.http import HttpRequest
2
  from django.shortcuts import render, redirect
3
  from django.urls import reverse
4
+ from tecnicas.models import Dato, Participacion
5
+ from tecnicas.controllers import SesionController
 
6
  from .monitor_controller import MonitorController
7
 
8
 
9
  class MonitorEscalasController(MonitorController):
 
 
10
  def __init__(self, session: SesionController):
11
  super().__init__(session)
12
+ self.url_view = "tecnicas/manage_sesions/monitor-sesion.html"
13
+ self.previus_view = "cata_system:detalles_sesion"
14
 
15
+ def controllPostFinishSession(self, request: HttpRequest):
 
 
 
 
 
 
 
 
 
 
16
  self.setContext()
17
+ (is_all_end, message) = self.checkAllFinish()
18
  if not is_all_end:
19
  self.context["error"] = message
20
+ return render(request, self.url_view, self.context)
21
  response = self.finishSession()
22
  if isinstance(response, dict):
23
  self.context["error"] = response["error"]
24
+ return render(request, self.url_view, self.context)
25
  self.context["message"] = message
26
+ return redirect(reverse(self.previus_view, kwargs={"session_code": self.sensorial_session.codigo_sesion}))
 
 
 
 
27
 
28
+ def checkAllFinish(self):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  technique = self.sensorial_session.tecnica
30
 
31
+ expected_ratings_repetition = self.getExpectedRatingsEscalasRapida()
32
 
33
+ all_participations = list(
34
+ Participacion.objects.filter(tecnica=technique))
35
 
36
  if len(all_participations) < technique.limite_catadores:
37
  return (False, "No se ha alcanzado el número máximo de Catadores")
38
 
39
  for particiapation in all_participations:
40
+ num_ratings_now = Dato.objects.filter(
41
+ id_calificacion__num_repeticion=technique.repeticion,
42
+ id_calificacion__id_catador=particiapation.catador,
43
+ id_calificacion__id_tecnica=technique
44
+ ).count()
45
 
46
  if num_ratings_now < expected_ratings_repetition:
47
  return (False, "No todos los catadores han finalizado su evaluación")
48
 
49
  return (True, "Puedes finalizar la sesión")
 
 
 
 
 
 
 
tecnicas/controllers/views_controller/session_management/monitor_rata_controller.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import Dato, Participacion
5
+ from tecnicas.controllers import SesionController
6
+ from .monitor_controller import MonitorController
7
+
8
+
9
+ class MonitorRATAController(MonitorController):
10
+ def __init__(self, session: SesionController):
11
+ super().__init__(session)
12
+ self.url_view = "tecnicas/manage_sesions/monitor-sesion.html"
13
+ self.previus_view = "cata_system:detalles_sesion"
14
+
15
+ def controllPostFinishSession(self, request: HttpRequest):
16
+ self.setContext()
17
+ (is_all_end, message) = self.checkAllFinish()
18
+ if not is_all_end:
19
+ self.context["error"] = message
20
+ return render(request, self.url_view, self.context)
21
+ response = self.finishSession()
22
+ if isinstance(response, dict):
23
+ self.context["error"] = response["error"]
24
+ return render(request, self.url_view, self.context)
25
+ self.context["message"] = message
26
+ return redirect(reverse(self.previus_view, kwargs={"session_code": self.sensorial_session.codigo_sesion}))
27
+
28
+ def checkAllFinish(self):
29
+ technique = self.sensorial_session.tecnica
30
+
31
+ expected_ratings_repetition = self.getExpectedRatingsEscalasRapida()
32
+
33
+ all_participations = list(
34
+ Participacion.objects.filter(tecnica=technique))
35
+
36
+ for particiapation in all_participations:
37
+ num_ratings_now = Dato.objects.filter(
38
+ id_calificacion__num_repeticion=technique.repeticion,
39
+ id_calificacion__id_catador=particiapation.catador,
40
+ id_calificacion__id_tecnica=technique
41
+ ).count()
42
+
43
+ if num_ratings_now < expected_ratings_repetition:
44
+ return (False, "No todos los catadores han finalizado su evaluación")
45
+
46
+ return (True, "Puedes finalizar la sesión")
tecnicas/controllers/views_controller/sessions_tester/convencional_scales_controller.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.models import SesionSensorial, Catador, Participacion, Producto, Calificacion, Palabra
5
+ from tecnicas.controllers import PosicionController, CalificacionController, ParticipacionController, PalabrasController, EscalaController, DatoController
6
+
7
+
8
+ class ConvencionalScalesController:
9
+ context = {}
10
+ current_directory = "tecnicas/forms_tester/convencional.html"
11
+ previus_directory = "cata_system:catador_init_session"
12
+
13
+ def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
14
+ self.tester = user_tester
15
+ self.session = sensorial_session
16
+
17
+ def controllGetEscalas(self, request: HttpRequest):
18
+ technique = self.session.tecnica
19
+ self.participation = Participacion.objects.get(
20
+ tecnica=technique, catador=request.user.user_catador)
21
+
22
+ ctx = self.context
23
+ ctx["session"] = self.session
24
+
25
+ # Obtener posiciones y palabras de la técnica
26
+ positions_in_order = PosicionController.getPostionsInOrder(
27
+ id_order=request.session["id_order"])
28
+ aligned_positions_in_order = sorted(
29
+ positions_in_order, key=lambda p: p.posicion)
30
+ words = PalabrasController.getWordsInTechnique(technique=technique)
31
+
32
+ # Comprobar siguiente posición sin calificar
33
+ (next_position, end_products) = CalificacionController.checkPositionWithoutRating(
34
+ positions=aligned_positions_in_order,
35
+ user_cata=request.user.user_catador,
36
+ repetition=technique.repeticion,
37
+ technique=technique,
38
+ num_words=len(words)
39
+ )
40
+
41
+ # Si no hay productos se finaliza la sesion
42
+ if end_products:
43
+ ParticipacionController.finishSession(self.participation)
44
+ params = {"code_sesion": self.session.codigo_sesion}
45
+ return redirect(reverse('cata_system:catador_init_session', kwargs=params))
46
+
47
+ # Si devuelve una lista, tomar el primer elemento
48
+ if isinstance(next_position, list):
49
+ next_position = next_position[0]
50
+
51
+ # Producto a calificar ahora
52
+ product = next_position.id_producto
53
+ ctx["product"] = product
54
+
55
+ # Revisar las palabras para calificar
56
+ try:
57
+ rating = Calificacion.objects.get(
58
+ num_repeticion=technique.repeticion,
59
+ id_producto=product,
60
+ id_tecnica=technique,
61
+ id_catador=self.tester
62
+ )
63
+ there_rating = True
64
+ except Calificacion.DoesNotExist:
65
+ there_rating = False
66
+
67
+ # Si no hay calificaciones previas, usar todas las palabras
68
+ if not there_rating:
69
+ ctx["words"] = words
70
+ else:
71
+ ratings_product = rating.dato_calificacion.all()
72
+ # Filtrar palabras que faltan
73
+ words_to_use = PalabrasController.getWordsWithoutData(
74
+ recoreded_data=ratings_product,
75
+ words=words
76
+ )
77
+ ctx["words"] = words_to_use
78
+
79
+ # Escala y etiquetas relacionadas
80
+ scale = EscalaController.getScaleByTechnique(technique=technique)
81
+ ctx["scale"] = scale
82
+ ctx["type_scale"] = scale.id_tipo_escala.nombre_escala
83
+ ctx["tags"] = EscalaController.getRelatedTagsInScale(scale=scale)
84
+
85
+ return render(request, self.current_directory, ctx)
86
+
87
+ def controllGetRATA(self, request: HttpRequest):
88
+ technique = self.session.tecnica
89
+ self.participation = Participacion.objects.get(
90
+ tecnica=technique, catador=request.user.user_catador)
91
+
92
+ self.context["session"] = self.session
93
+
94
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
95
+
96
+ words = PalabrasController.getWordsInTechnique(technique=technique)
97
+
98
+ use_product: Producto = None
99
+ use_words: list[Palabra] = None
100
+
101
+ # Revisamos el producto que le falten calificaciones
102
+ for current_product in products_in_technique:
103
+ try:
104
+ rating = Calificacion.objects.get(
105
+ num_repeticion=technique.repeticion,
106
+ id_producto=current_product,
107
+ id_tecnica=technique,
108
+ id_catador=self.tester
109
+ )
110
+ there_rating = True
111
+ except Calificacion.DoesNotExist:
112
+ there_rating = False
113
+
114
+ # Si no hay calificacion mandamos el producto actual y todas la palabras
115
+ if not there_rating:
116
+ use_product = current_product
117
+ use_words = words
118
+ break
119
+
120
+ # Obtener los datos asociados para la calificacion para ver que palabras quedan por calificar
121
+ recoreded_data = rating.dato_calificacion.all()
122
+
123
+ if not recoreded_data:
124
+ # Si no hay datos entonces devolver el producto con todas las palabras
125
+ use_product = current_product
126
+ use_words = words
127
+ break
128
+ else:
129
+ words_to_use = PalabrasController.getWordsWithoutData(
130
+ recoreded_data=recoreded_data, words=words)
131
+
132
+ # Si quedan palabras por calificar mandar las palabras con el producto
133
+ if not isinstance(words_to_use, dict) and words_to_use:
134
+ use_product = current_product
135
+ use_words = words_to_use
136
+ break
137
+
138
+ # Si no hay producto que falta por calificar finalizar sesion para el Catador
139
+ if not use_product:
140
+ updated_participation = ParticipacionController.finishSession(
141
+ self.participation)
142
+ params = {
143
+ "code_sesion": self.session.codigo_sesion
144
+ }
145
+ return redirect(reverse(self.previus_directory, kwargs=params))
146
+
147
+ self.context["product"] = use_product
148
+ self.context["words"] = use_words
149
+
150
+ # Agregar informacion de la escala
151
+ scale = EscalaController.getScaleByTechnique(technique=technique)
152
+ self.context["scale"] = scale
153
+ self.context["type_scale"] = scale.id_tipo_escala.nombre_escala
154
+
155
+ use_tags = EscalaController.getRelatedTagsInScale(scale=scale)
156
+ self.context["tags"] = use_tags
157
+
158
+ return render(request, self.current_directory, self.context)
tecnicas/controllers/views_controller/{main_tester_form_controller.py → sessions_tester/init_session_tester_controller.py} RENAMED
@@ -1,28 +1,101 @@
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
 
5
 
6
- class MainTesterFormController():
7
  tester: Catador
8
  session: SesionSensorial
9
  order: Orden | dict
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):
17
- return controller_error("Parámetros inexistentes")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  def assignOrder(self):
20
  with transaction.atomic():
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
 
@@ -44,15 +117,16 @@ class MainTesterFormController():
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
  #
54
- # Si numero_calificaciones_esperadas = num_productos * num_palabras
55
- # Es igual a numero_calificaciones_actuales en la repetcion R
56
  # Ha terminado la repeticion
57
  #
58
  # ////////////////////////////////////////////////////////////// #
@@ -61,23 +135,29 @@ class MainTesterFormController():
61
  num_products = Producto.objects.filter(
62
  id_tecnica=self.session.tecnica).count()
63
 
64
- style_words = self.session.tecnica.id_estilo
 
65
 
66
  num_words: int
67
 
68
- if style_words.nombre_estilo == "atributos":
69
  num_words = EsAtributo.objects.get(
70
  id_tecnica=self.session.tecnica).palabras.count()
71
- elif style_words.nombre_estilo == "vocabulario":
72
  num_words = EsVocabulario.objects.get(
73
  id_tecnica=self.session.tecnica).id_vocabulario.palabras.count()
74
 
75
- num_ratings_now = Calificacion.objects.filter(
76
- id_tecnica=self.session.tecnica, id_catador=self.tester, num_repeticion=repetition).count()
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:
 
 
 
1
  from django.db import transaction
2
+ from django.http import HttpRequest
3
+ from django.shortcuts import render, redirect
4
+ from django.urls import reverse
5
+ from tecnicas.models import Catador, SesionSensorial, Orden, Participacion, Producto, EsAtributo, Calificacion, EsVocabulario, Dato
6
+ from tecnicas.controllers import ParticipacionController
7
+ from tecnicas.utils import controller_error, shuffleArray
8
 
9
 
10
+ class InitSessionTesterController():
11
  tester: Catador
12
  session: SesionSensorial
13
  order: Orden | dict
14
+ current_direction = "tecnicas/forms_tester/init_session.html"
15
+ escalas_direction = "cata_system:session_convencional"
16
+
17
+ def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
18
+ self.tester = user_tester
19
+ self.session = sensorial_session
20
+
21
+ def controllGetEscalas(self, request: HttpRequest):
22
+ context = {
23
+ "session": self.session,
24
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
25
+ }
26
+
27
+ order = self.checkAndAssignOrder()
28
+ if isinstance(order, dict):
29
+ context["error"] = order["error"]
30
+ return render(request, self.current_direction, context)
31
+
32
+ is_end = self.isEndedSessionEscalas()
33
+
34
+ request.session["id_order"] = order.id
35
+ context["has_ended"] = is_end
36
+
37
+ if is_end:
38
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
39
+
40
+ if "error" in request.GET:
41
+ context["error"] = request.GET["error"]
42
+
43
+ return render(request, self.current_direction, context)
44
+
45
+ def controllPostEscalas(self, request: HttpRequest):
46
+ context = {
47
+ "session": self.session,
48
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
49
+ }
50
+
51
+ if request.POST["action"] == "start_posting":
52
+ parameters = {
53
+ "code_sesion": self.session.codigo_sesion
54
+ }
55
+
56
+ is_end = self.isEndedSessionEscalas()
57
+ if is_end:
58
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
59
+ return render(request, self.current_direction, context)
60
+
61
+ update_participation = ParticipacionController.enterSession(
62
+ tester=request.user.user_catador, session=self.session)
63
+ if isinstance(update_participation, dict):
64
+ context["error"] = update_participation["error"]
65
+ return render(request, self.current_direction, context)
66
+
67
+ request.session["id_participation"] = update_participation.id
68
+ return redirect(reverse(self.escalas_direction, kwargs=parameters))
69
+ elif request.POST["action"] == "exit_session":
70
+ response = ParticipacionController.outSession(
71
+ tester=request.user.user_catador, session=self.session)
72
+ if isinstance(response, dict):
73
+ context["error"] = response["error"]
74
+ return render(request, self.current_direction, context)
75
+ else:
76
+ context["error"] = "Acción sin especificar"
77
+ return render(request, self.current_direction, context)
78
+
79
+ def controllGetRATA(self, request: HttpRequest):
80
+ context = {
81
+ "session": self.session,
82
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
83
+ }
84
+
85
+ is_end = self.isEndedSessionEscalas()
86
+
87
+ context["has_ended"] = is_end
88
+
89
+ if is_end:
90
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
91
+
92
+ return render(request, self.current_direction, context)
93
 
94
  def assignOrder(self):
95
  with transaction.atomic():
96
  orders_without_tester = list(Orden.objects.select_for_update().filter(
97
  id_tecnica=self.session.tecnica, id_catador=None))
98
 
 
 
99
  if not orders_without_tester:
100
  return controller_error("Las ordenes se han acabado")
101
 
 
117
  return create
118
  return self.order_to_assign
119
 
120
+ def isEndedSessionEscalas(self):
121
  try:
122
  participation = Participacion.objects.get(
123
  catador=self.tester, tecnica=self.session.tecnica)
124
+ self.session.refresh_from_db()
125
 
126
  # ////////////////////////////////////////////////////////////// #
127
  #
128
+ # numero_datos_esperadas = num_productos * num_palabras
129
+ # Si numero_datos_esperadas es igual a numero_datos_actuales en la repetcion R
130
  # Ha terminado la repeticion
131
  #
132
  # ////////////////////////////////////////////////////////////// #
 
135
  num_products = Producto.objects.filter(
136
  id_tecnica=self.session.tecnica).count()
137
 
138
+ technique = self.session.tecnica
139
+ style_words = technique.id_estilo.nombre_estilo
140
 
141
  num_words: int
142
 
143
+ if style_words == "atributos":
144
  num_words = EsAtributo.objects.get(
145
  id_tecnica=self.session.tecnica).palabras.count()
146
+ elif style_words == "vocabulario":
147
  num_words = EsVocabulario.objects.get(
148
  id_tecnica=self.session.tecnica).id_vocabulario.palabras.count()
149
 
 
 
 
150
  expected_ratings_repetition = num_products * num_words
151
 
152
+ num_ratings_now = Dato.objects.filter(
153
+ id_calificacion__id_catador=self.tester,
154
+ id_calificacion__id_tecnica=technique,
155
+ id_calificacion__num_repeticion=technique.repeticion
156
+ ).count()
157
+
158
+ is_end = num_ratings_now >= expected_ratings_repetition
159
+
160
+ return is_end
161
  else:
162
  return participation.finalizado
163
  except Participacion.DoesNotExist:
tecnicas/controllers/views_controller/{list_sessions_tester_controller.py → sessions_tester/list_sessions_tester_controller.py} RENAMED
File without changes
tecnicas/controllers/views_controller/{login_tester_controller.py → sessions_tester/login_session_tester_controller.py} RENAMED
@@ -6,7 +6,7 @@ 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
@@ -33,38 +33,38 @@ class LoginTesterController():
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 = {}
 
6
  from tecnicas.utils import controller_error
7
 
8
 
9
+ class LoginSessionTesterController():
10
  tester: Catador
11
  session: SesionSensorial
12
  taster_participation: Participacion
 
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
+ with transaction.atomic():
44
+ code_session = self.session.codigo_sesion
45
+ self.session = SesionSensorial.objects.select_for_update().get(
46
+ codigo_sesion=code_session)
 
 
 
47
 
48
+ max_testers = self.session.tecnica.limite_catadores
49
+ current_num_testers = Participacion.objects.filter(
50
+ tecnica=self.session.tecnica).count()
51
 
52
+ if current_num_testers >= max_testers:
53
+ context["error"] = "La sesión ha alcanzado el número máximo de catadores"
54
+ return render(request, self.current_direcction, context)
55
 
56
+ self.taster_participation = Participacion.objects.create(
57
+ tecnica=self.session.tecnica,
58
+ catador=self.tester,
59
+ finalizado=False
60
+ )
61
+ params = {
62
+ "code_sesion": self.session.codigo_sesion
63
+ }
64
+ return redirect(reverse(self.destinity_direcction, kwargs=params))
65
+ else:
66
+ context["error"] = "Ya no es posible ingresar a la sesión"
67
+ return render(request, self.current_direcction, context)
68
 
69
  def validateEntryRATA(self, request: HttpRequest):
70
  context = {}
tecnicas/controllers/views_controller/vocabulary_manage/create_vocabulary_controller.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.shortcuts import render
2
+ from django.db import IntegrityError
3
+ from django.http import HttpRequest
4
+ from tecnicas.forms import WordForm
5
+ from tecnicas.models import Vocabulario, Palabra
6
+ import json
7
+
8
+
9
+ class CreateVocabularyController():
10
+ context = {}
11
+ current_url = "tecnicas/manage_vocabulary/create-vocabulary.html"
12
+
13
+ def __init__(self, form_word: WordForm = WordForm(), list_words: list = []):
14
+ self.context["form_word"] = form_word
15
+ self.context["words"] = list_words
16
+
17
+ def controllGet(self, request: HttpRequest):
18
+ self.context = {
19
+ "form_word": WordForm(),
20
+ "words": []
21
+ }
22
+
23
+ if "name_voca" in request.GET:
24
+ current = Vocabulario.objects.get(
25
+ nombre_vocabulario=request.GET["name_voca"])
26
+ self.context["name_vacabulary"] = current.nombre_vocabulario
27
+ self.context["words"] = current.palabras.all()
28
+
29
+ return render(request, self.current_url, self.context)
30
+
31
+ def controllPost(self, request: HttpRequest):
32
+ self.context = {
33
+ "form_word": self.context["form_word"],
34
+ "words": self.context["words"],
35
+ }
36
+
37
+ if "nombre_vocabulario" not in request.POST:
38
+ self.context["error"] = "Nombre de vocabulario requerido"
39
+ return render(request, self.current_url, self.context)
40
+
41
+ new_vocabulary_name = request.POST.get("nombre_vocabulario").strip()
42
+ is_update = request.POST.get("is_update")
43
+
44
+ print(is_update)
45
+
46
+ if is_update:
47
+ if "original_name" not in request.POST:
48
+ self.context["error"] = "Nombre original de vocabulario requerido"
49
+ return render(request, self.current_url, self.context)
50
+
51
+ original_name = request.POST["original_name"].strip()
52
+
53
+ if original_name != new_vocabulary_name:
54
+ if Vocabulario.objects.filter(nombre_vocabulario=new_vocabulary_name).exists():
55
+ self.context["error"] = "Ya existe un vocabulario con el nombre pasado"
56
+ return render(request, self.current_url, self.context)
57
+
58
+ try:
59
+ current_vocababulary = Vocabulario.objects.get(
60
+ nombre_vocabulario=original_name)
61
+ except Vocabulario.DoesNotExist:
62
+ self.context["error"] = "No existe un vocabulario con ese nombre"
63
+ return render(request, self.current_url, self.context)
64
+
65
+ words_json = request.POST.get("words", "")
66
+ if words_json:
67
+ try:
68
+ words_list = json.loads(words_json)
69
+
70
+ ids = [int(w.get("id", 0))
71
+ for w in words_list if str(w.get("id", "")).isdigit()]
72
+
73
+ words = Palabra.objects.filter(id__in=ids)
74
+
75
+ current_vocababulary.palabras.set(words)
76
+ current_vocababulary.nombre_vocabulario = new_vocabulary_name
77
+ current_vocababulary.save()
78
+ except (json.JSONDecodeError, ValueError):
79
+ self.context["error"] = 'Ocurrió un error al revisar las palabras, revise “Ver vocabularios”, para reasignar las palabras'
80
+ return render(request, self.current_url, self.context)
81
+
82
+ self.context["message"] = 'Vocabulario creado con éxito, puedes revisarlo en "Ver vocabularios"'
83
+ return render(request, self.current_url, self.context)
84
+ elif not is_update:
85
+ try:
86
+ new_vocababulary = Vocabulario.objects.create(
87
+ nombre_vocabulario=new_vocabulary_name)
88
+ except IntegrityError:
89
+ self.context["error"] = "Ya existe un vocabulario con ese nombre"
90
+ return render(request, self.current_url, self.context)
91
+
92
+ words_json = request.POST.get("words", "")
93
+ if words_json:
94
+ try:
95
+ words_list = json.loads(words_json)
96
+
97
+ ids = [int(w.get("id", 0))
98
+ for w in words_list if str(w.get("id", "")).isdigit()]
99
+
100
+ words = Palabra.objects.filter(id__in=ids)
101
+
102
+ new_vocababulary.palabras.add(*words)
103
+ except (json.JSONDecodeError, ValueError):
104
+ self.context["error"] = 'Ocurrió un error al revisar las palabras, revise “Ver vocabularios”, para reasignar las palabras'
105
+ return render(request, self.current_url, self.context)
106
+
107
+ self.context["message"] = 'Vocabulario actualziado con éxito, puedes revisarlo en "Ver vocabularios"'
108
+ return render(request, self.current_url, self.context)
109
+ else:
110
+ pass
tecnicas/controllers/views_controller/vocabulary_manage/list_vocabulary_controller.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.shortcuts import render
2
+ from django.http import HttpRequest
3
+ from django.core.paginator import Paginator, PageNotAnInteger
4
+ from tecnicas.models import Vocabulario
5
+ from tecnicas.utils import controller_error
6
+
7
+
8
+ class ListVocabularyController():
9
+ current_url = "tecnicas/manage_vocabulary/list-vocabulary.html"
10
+
11
+ def __init__(self):
12
+ pass
13
+
14
+ def controllGet(self, request: HttpRequest, page: int):
15
+ context = {}
16
+
17
+ info_element_page = self.getVocabularys(page)
18
+
19
+ if isinstance(info_element_page, dict):
20
+ context["error"] = info_element_page["error"]
21
+ return render(request, self.current_url, context)
22
+
23
+ (vocabularies_in_page, is_last_page, current_page) = info_element_page
24
+
25
+ context["vocabularies"] = vocabularies_in_page
26
+ context["last_page"] = is_last_page
27
+ context["num_page"] = current_page
28
+
29
+ return render(request, self.current_url, context)
30
+
31
+ def getVocabularys(self, num_page: int):
32
+ elements_by_page = 6
33
+
34
+ queryset = Vocabulario.objects.all().order_by('-creado')
35
+
36
+ paginator = Paginator(queryset, elements_by_page)
37
+ try:
38
+ vocabularies_in_page = paginator.page(num_page)
39
+ except PageNotAnInteger:
40
+ return controller_error("índice inválido")
41
+
42
+ if not vocabularies_in_page.object_list:
43
+ return controller_error("Sin registros de Participaciones")
44
+
45
+ current_page = vocabularies_in_page.number
46
+ is_last_page = not current_page < paginator.num_pages
47
+
48
+ return (vocabularies_in_page, is_last_page, current_page)
tecnicas/forms/__init__.py CHANGED
@@ -1,6 +1,9 @@
1
- from .sesion_basic_form import SesionBasicForm
2
- from .sesion_tags_form import SesionTagsForm
 
 
3
  from .etiqueta_form import EtiquetaForm
4
  from .codes_form import CodesForm
5
  from .catador_form import CatadorForm
6
- from .word_form import WordForm
 
 
1
+ from .create_session.sesion_basic_form import SesionBasicForm
2
+ from .create_session.sesiob_basic_cata_form import SesionBasicCATAForm
3
+ from .create_session.sesion_tags_form import SesionTagsForm
4
+
5
  from .etiqueta_form import EtiquetaForm
6
  from .codes_form import CodesForm
7
  from .catador_form import CatadorForm
8
+ from .word_form import WordForm
9
+ from .vocabulary_select import VocabularioSelectForm
tecnicas/forms/create_session/sesiob_basic_cata_form.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django import forms
2
+ from tecnicas.models import EstiloPalabra
3
+
4
+
5
+ class SesionBasicCATAForm(forms.Form):
6
+ nombre_sesion = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
7
+ "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
8
+ "name": "nombre_sesion",
9
+ "placeholder": "Ej. Mermelada de mango picante"
10
+ }), required=False)
11
+
12
+ numero_productos = forms.IntegerField(widget=forms.NumberInput(attrs={
13
+ "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
14
+ "placeholder": "Solo números"
15
+ }), required=True)
16
+
17
+ instrucciones = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
18
+ "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
19
+ "placeholder": "Este campo es opcional"
20
+ }), required=False)
21
+
22
+ def __init__(self, *args, **kwargs):
23
+ super().__init__(*args, **kwargs)
24
+
25
+ self.fields['estilo_palabras'] = forms.ModelChoiceField(queryset=EstiloPalabra.objects.all(), widget=forms.RadioSelect(attrs={
26
+ "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
27
+ }), required=True, initial=EstiloPalabra.objects.first())
tecnicas/forms/{sesion_basic_form.py → create_session/sesion_basic_form.py} RENAMED
@@ -1,7 +1,5 @@
1
  from django import forms
2
- from ..models import TipoEscala
3
- from ..models import TipoTecnica
4
- from ..models import EstiloPalabra
5
 
6
 
7
  class SesionBasicForm(forms.Form):
 
1
  from django import forms
2
+ from tecnicas.models import TipoEscala, EstiloPalabra
 
 
3
 
4
 
5
  class SesionBasicForm(forms.Form):
tecnicas/forms/{sesion_tags_form.py → create_session/sesion_tags_form.py} RENAMED
@@ -1,5 +1,5 @@
1
  from django import forms
2
- from ..models import Etiqueta
3
 
4
 
5
  class SesionTagsForm(forms.Form):
 
1
  from django import forms
2
+ from tecnicas.models import Etiqueta
3
 
4
 
5
  class SesionTagsForm(forms.Form):
tecnicas/forms/vocabulary_select.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django import forms
2
+ from tecnicas.models import Vocabulario
3
+
4
+
5
+ class VocabularioSelectForm(forms.Form):
6
+ vocabulario = forms.ModelChoiceField(
7
+ queryset=Vocabulario.objects.all(),
8
+ required=True,
9
+ label="Selecciona un vocabulario",
10
+ empty_label="-- Selecciona uno --",
11
+ widget=forms.Select(attrs={
12
+ "class": "w-full border rounded p-4 bg-surface-sweet",
13
+ "id": "vocabulario"
14
+ })
15
+ )
tecnicas/migrations/0021_rename_nomre_vocabulario_vocabulario_nombre_vocabulario_and_more.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-11-06 21:46
2
+
3
+ import django.utils.timezone
4
+ import shortuuid.main
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('tecnicas', '0020_remove_catador_apellido_remove_catador_correo_and_more'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.RenameField(
16
+ model_name='vocabulario',
17
+ old_name='nomre_vocabulario',
18
+ new_name='nombre_vocabulario',
19
+ ),
20
+ migrations.AddField(
21
+ model_name='vocabulario',
22
+ name='creado',
23
+ field=models.DateTimeField(default=django.utils.timezone.now),
24
+ ),
25
+ migrations.AlterField(
26
+ model_name='sesionsensorial',
27
+ name='codigo_sesion',
28
+ field=models.CharField(default=shortuuid.main.ShortUUID.uuid, editable=False, max_length=22, primary_key=True, serialize=False),
29
+ ),
30
+ ]
tecnicas/models/calificacion.py CHANGED
@@ -8,4 +8,7 @@ class Calificacion(models.Model):
8
  num_repeticion = models.IntegerField()
9
  id_producto = models.ForeignKey(Producto, on_delete=models.CASCADE, related_name="calificacion_producto")
10
  id_tecnica = models.ForeignKey(Tecnica, on_delete=models.CASCADE, related_name="calificacion_tecnica")
11
- id_catador = models.ForeignKey(Catador, on_delete=models.CASCADE, related_name="calificacion_catador")
 
 
 
 
8
  num_repeticion = models.IntegerField()
9
  id_producto = models.ForeignKey(Producto, on_delete=models.CASCADE, related_name="calificacion_producto")
10
  id_tecnica = models.ForeignKey(Tecnica, on_delete=models.CASCADE, related_name="calificacion_tecnica")
11
+ id_catador = models.ForeignKey(Catador, on_delete=models.CASCADE, related_name="calificacion_catador")
12
+
13
+ def __str__(self):
14
+ return f"{self.id} - {self.id_tecnica.sesion_tecnica} - {self.num_repeticion} - {self.id_catador.user.username}"
tecnicas/models/dato.py CHANGED
@@ -11,4 +11,4 @@ class Dato(models.Model):
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}"
 
11
  Calificacion, on_delete=models.CASCADE, related_name="dato_calificacion")
12
 
13
  def __str__(self):
14
+ return f"{self.id_calificacion.id_tecnica.sesion_tecnica}- {self.id_calificacion.num_repeticion} - {self.id_palabra.nombre_palabra} - {self.id_calificacion.id_producto.codigoProducto} - {self.id_calificacion.id_catador.user.username}"
tecnicas/models/dato_valor.py CHANGED
@@ -9,7 +9,7 @@ class ValorDecimal(models.Model):
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):
@@ -18,4 +18,4 @@ class ValorBooleano(models.Model):
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}"
 
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.user.username}"
13
 
14
 
15
  class ValorBooleano(models.Model):
 
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.user.username}"
tecnicas/models/vocabulario.py CHANGED
@@ -1,10 +1,13 @@
1
  from django.db import models
2
-
3
  from .palabra import Palabra
4
 
 
5
  class Vocabulario(models.Model):
6
- nomre_vocabulario = models.CharField(max_length=255, unique=True)
7
- palabras = models.ManyToManyField(Palabra, related_name="vovabulario_palabras")
 
 
8
 
9
  def __str__(self):
10
- return self.nomre_vocabulario
 
1
  from django.db import models
2
+ from django.utils import timezone
3
  from .palabra import Palabra
4
 
5
+
6
  class Vocabulario(models.Model):
7
+ nombre_vocabulario = models.CharField(max_length=255, unique=True)
8
+ palabras = models.ManyToManyField(
9
+ Palabra, related_name="vovabulario_palabras")
10
+ creado = models.DateTimeField(default=timezone.now)
11
 
12
  def __str__(self):
13
+ return self.nombre_vocabulario
tecnicas/static/img/letters.webp ADDED
tecnicas/static/js/choose-vocabulary.js ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener("DOMContentLoaded", () => {
2
+ const vocabSelect = document.getElementById("vocabulario");
3
+ const wordsList = document.getElementById("palabras-lista");
4
+ const formNextStep = document.getElementById("cts-create-session");
5
+ let vocabularyId = "";
6
+
7
+ vocabSelect.addEventListener("change", async (e) => {
8
+ const vocabId = e.target.value;
9
+ vocabularyId = vocabId;
10
+ console.log(e.target);
11
+ console.log(vocabId);
12
+
13
+ wordsList.innerHTML =
14
+ "<li class='text-gray-500 text-center'>Cargando...</li>";
15
+
16
+ if (!vocabId) {
17
+ wordsList.innerHTML =
18
+ "<li class='text-gray-500 text-center'>Selecciona un vocabulario para ver sus palabras</li>";
19
+ return;
20
+ }
21
+
22
+ url_fetch = `api/vocabulario/${vocabId}/palabras`;
23
+
24
+ try {
25
+ const response = await fetch(url_fetch, { method: "GET" });
26
+ if (!response.ok) throw new Error("Error en la petición");
27
+ const json_response = await response.json();
28
+
29
+ words = json_response.data.words;
30
+
31
+ if (words === 0) {
32
+ wordsList.innerHTML =
33
+ "<li class='text-center text-lg'>No hay palabras asociadas</li>";
34
+ return;
35
+ }
36
+
37
+ wordsList.innerHTML = "";
38
+ words.forEach((p) => {
39
+ const li = document.createElement("li");
40
+ li.textContent = p.nombre_palabra;
41
+ li.className =
42
+ "bg-surface-sweet text-black rounded font-bold text-lg px-4 py-3";
43
+ wordsList.appendChild(li);
44
+ });
45
+ } catch (err) {
46
+ wordsList.innerHTML =
47
+ "<li class='text-red-500 text-center text-lg'>Error al cargar las palabras</li>";
48
+ console.error(err);
49
+ }
50
+ });
51
+
52
+ formNextStep.addEventListener("submit", (e) => {
53
+ if (!vocabularyId) {
54
+ e.preventDefault();
55
+ wordsList.innerHTML =
56
+ "<li class='text-center text-lg'>Seleccione un Vocabulario</li>";
57
+ return;
58
+ }
59
+
60
+ const useVocabulary = document.createElement("input");
61
+ useVocabulary.type = "hidden";
62
+ useVocabulary.name = "vocabulario";
63
+ useVocabulary.value = vocabularyId;
64
+
65
+ formNextStep.appendChild(useVocabulary);
66
+ });
67
+ });
tecnicas/static/js/created-scale.js CHANGED
@@ -54,11 +54,13 @@ async function sendRating(word) {
54
  .querySelector(".id-product").textContent;
55
 
56
  const idWord = formRatingWord.querySelector(".id-word").textContent;
 
57
 
58
  dataForm.set("code-product", codeProduct);
59
  dataForm.set("id-product", idProduct);
60
  dataForm.set("name-word", word);
61
  dataForm.set("id-word", idWord);
 
62
 
63
  try {
64
  const respone = await fetch(url, {
 
54
  .querySelector(".id-product").textContent;
55
 
56
  const idWord = formRatingWord.querySelector(".id-word").textContent;
57
+ const idTechnique = document.querySelector(".ct-input-id-tech").value
58
 
59
  dataForm.set("code-product", codeProduct);
60
  dataForm.set("id-product", idProduct);
61
  dataForm.set("name-word", word);
62
  dataForm.set("id-word", idWord);
63
+ dataForm.set("id-technique", idTechnique);
64
 
65
  try {
66
  const respone = await fetch(url, {
tecnicas/static/js/created-vocabulary.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // **************************************
2
+ // Create Vocabulary
3
+ // **************************************
4
+ async function submitSelectWords(classNanmeForm, update = false) {
5
+ const form = document.querySelector(`.${classNanmeForm}`);
6
+
7
+ const name_vocabulary = form.querySelector(".cts-name-voca").value;
8
+ if (!name_vocabulary || name_vocabulary == "") {
9
+ spanNotificationRed("Se requiere el nombre del vocabulario");
10
+ return;
11
+ }
12
+
13
+ if (listWordsSelect.length === 0) {
14
+ spanNotificationRed("Debe seleccionar al menos una palabra");
15
+ return;
16
+ }
17
+
18
+ const wordsInput = document.createElement("input");
19
+ wordsInput.type = "hidden";
20
+ wordsInput.name = "words";
21
+ wordsInput.value = JSON.stringify(listWordsSelect);
22
+
23
+ const [isUpdata, orinalName] = inputIsUpdateVocabulary(update);
24
+
25
+ form.appendChild(wordsInput);
26
+ form.appendChild(isUpdata);
27
+ if (orinalName) form.appendChild(orinalName);
28
+
29
+ form.submit();
30
+ }
31
+
32
+ function inputIsUpdateVocabulary(is_update = false) {
33
+ const isUpdata = document.createElement("input");
34
+ isUpdata.type = "hidden";
35
+ isUpdata.name = "is_update";
36
+ isUpdata.value = is_update;
37
+
38
+ if (is_update) {
39
+ const orinalName = document.querySelector(".cts-original-name").textContent;
40
+ const inputName = document.createElement("input");
41
+ inputName.type = "hidden";
42
+ inputName.name = "original_name";
43
+ inputName.value = orinalName;
44
+ return [isUpdata, inputName];
45
+ }
46
+
47
+ return [isUpdata, is_update];
48
+ }
tecnicas/static/js/download-table-csv.js ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ const btn = document.getElementById("download-csv-btn");
3
+ if (!btn) return;
4
+
5
+ btn.addEventListener("click", function () {
6
+ // Try set the table in the page
7
+ let table = document.getElementById("convencional-table");
8
+ if (!table) {
9
+ const section = btn.closest("section");
10
+ if (section) table = section.querySelector("table");
11
+ }
12
+ if (!table) {
13
+ console.warn("No se encontró la tabla para descargar.");
14
+ return;
15
+ }
16
+
17
+ // helper to trim and normalize cell text
18
+ const cellText = (cell) => {
19
+ if (!cell) return "";
20
+ return String(cell.textContent || "").trim();
21
+ };
22
+
23
+ // Collect headers
24
+ const headers = [];
25
+ const ths = table.querySelectorAll("thead th");
26
+ ths.forEach((th) => headers.push(cellText(th)));
27
+
28
+ // Collect rows
29
+ const rows = [];
30
+ const trs = table.querySelectorAll("tbody tr");
31
+ trs.forEach((tr) => {
32
+ const cols = [];
33
+ const tds = tr.querySelectorAll("td");
34
+ tds.forEach((td) => cols.push(cellText(td)));
35
+ rows.push(cols);
36
+ });
37
+
38
+ // Convert to CSV string (escape quotes, wrap in quotes if needed)
39
+ const escapeValue = (val) => {
40
+ if (val == null) return "";
41
+ normalVal = val.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
42
+ const needsQuotes = /[",\n,]/.test(normalVal);
43
+ let v = String(normalVal).replace(/"/g, '""');
44
+ if (needsQuotes) v = `"${v}"`;
45
+ return v;
46
+ };
47
+
48
+ const lines = [];
49
+ if (headers.length) lines.push(headers.map(escapeValue).join(","));
50
+ rows.forEach((r) => lines.push(r.map(escapeValue).join(",")));
51
+
52
+ const csvContent = lines.join("\n");
53
+
54
+ // File name: data_{nombre_sesion or codigo_sesion}
55
+ const rawName = (btn.dataset.sessionName || "").trim();
56
+ const code = (btn.dataset.sessionCode || "").trim() || "session";
57
+ const namePart = rawName
58
+ ? rawName.replace(/[^a-zA-Z0-9-_áéíóúÁÉÍÓÚ ]/g, "").replace(/\s+/g, "_")
59
+ : code;
60
+ const fileName = `data_${namePart}.csv`;
61
+
62
+ // Create blob and force download
63
+ const blob = new Blob([csvContent], { type: "text/csv;charset=UTF-8;" });
64
+ if (navigator.msSaveBlob) {
65
+ navigator.msSaveBlob(blob, fileName);
66
+ } else {
67
+ const link = document.createElement("a");
68
+ const url = URL.createObjectURL(blob);
69
+ link.setAttribute("href", url);
70
+ link.setAttribute("download", fileName);
71
+ link.style.visibility = "hidden";
72
+ document.body.appendChild(link);
73
+ link.click();
74
+ document.body.removeChild(link);
75
+ URL.revokeObjectURL(url);
76
+ }
77
+ });
78
+ });
tecnicas/static/js/panel-basic-cata.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const descriptons = {
2
+ atributos:
3
+ "Con el estilo atributos elijes las palabras para evaluar los productos",
4
+ vocabulario:
5
+ "Los vocabularios son un conjunto de palabras específicas para evaluar aspectos de un producto",
6
+ };
7
+
8
+ const helpStyleWords = document.querySelector(".cts-help-style-words");
9
+ let radiosStyleWords;
10
+
11
+ function initRadiosStyleWords() {
12
+ radiosStyleWords = document.getElementsByName("estilo_palabras");
13
+
14
+ for (let index = 0; index < radiosStyleWords.length; index++) {
15
+ const radio = radiosStyleWords[index];
16
+
17
+ if (radio.checked) {
18
+ const radioOption = radio.parentElement.textContent.trim();
19
+ const textHelp = descriptons[radioOption];
20
+ helpStyleWords.textContent = textHelp;
21
+ }
22
+
23
+ radio.parentElement.addEventListener("click", (e) => {
24
+ const radioOption = radio.parentElement.textContent.trim();
25
+ const textHelp = descriptons[radioOption];
26
+ helpStyleWords.textContent = textHelp;
27
+ });
28
+ }
29
+ }
30
+
31
+ initRadiosStyleWords();
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 = `/cata/api/palabras?${params}`;
32
 
33
  try {
34
  const respone = await fetch(url, {
 
28
  palabra: dataForm.get("search").trim(),
29
  });
30
 
31
+ const url = `api/palabras?${params}`;
32
 
33
  try {
34
  const respone = await fetch(url, {
tecnicas/templates/tecnicas/components/form-scale-continue.html ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <form action="" method="post" class="form-rating-{{word}} w-full">
2
+ {% csrf_token %}
3
+ <article class="overflow-x-scroll w-full">
4
+ <article class="bg-surface-card p-6 rounded-lg mb-3 w-fit">
5
+ <label for="id-range-word-{{word}}"
6
+ class="text-xl font-bold tracking-wide block mb-6 first-letter:uppercase">{{ word }}</label>
7
+
8
+ <span class="hidden id-word">{{ word.id }}</span>
9
+
10
+ <section class="block">
11
+ <div class="relative mx-6">
12
+ <input type="range" type="range" min="0" max="1000" value="500" name="rating-word"
13
+ class="range range-md text-blue-400 [--range-bg:orange] [--range-thumb:blue] [--range-fill:0]"
14
+ style="width: {{scale.longitud}}cm;">
15
+
16
+ <div class="absolute top-0 left-0 w-0.5 transform -translate-x-1/2 h-full bg-red-500 z-10">
17
+ </div>
18
+
19
+ <div class="absolute top-1/5 left-1/2 w-0.5 transform -translate-x-1/2 h-4/5 bg-red-500 z-10">
20
+ </div>
21
+
22
+ <div class="absolute top-0 right-0 w-0.5 transform -translate-x-1/2 h-full bg-red-500 z-10">
23
+ </div>
24
+ </div>
25
+
26
+ <div class="flex justify-between mt-4">
27
+ <div class="flex flex-col items-start text-left w-[100px]">
28
+ <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
29
+ {{ tags.0.id_etiqueta }}
30
+ </span>
31
+ </div>
32
+ <div class="flex flex-col items-start text-center w-[100px]">
33
+ <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
34
+ {{ tags.1.id_etiqueta }}
35
+ </span>
36
+ </div>
37
+ <div class="flex flex-col items-start text-right w-[100px]">
38
+ <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
39
+ {{ tags.2.id_etiqueta }}
40
+ </span>
41
+ </div>
42
+ </div>
43
+ </section>
44
+ </article>
45
+ </article>
46
+ <article class="flex flex-col justify-center gap-2 items-end actions-{{word}}">
47
+ <section class="flex justify-end items-center gap-2 btns-container">
48
+ <button type="button" onclick="checkSendRating('{{word}}')"
49
+ class="ct-btn-check-{{word}} cts-btn-general-compress py-2 px-4 cts-btn-secondary btn-push">
50
+ ¿Guardar calificación?
51
+ </button>
52
+ <button type="button" onclick="sendRating('{{word}}')"
53
+ class="ct-btn-submit-{{word}} cts-btn-general-compress py-2 px-4 cts-btn-primary btn-push hidden">
54
+ Estoy seguro
55
+ </button>
56
+ <button type="button" onclick="cancelSendRating('{{word}}')"
57
+ class="ct-btn-cancel-{{word}} cts-btn-general-compress py-2 px-4 cts-btn-error btn-push hidden">
58
+ Cancelar
59
+ </button>
60
+ </section>
61
+ </article>
62
+ </form>
tecnicas/templates/tecnicas/components/form-scale-structure.html ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <form action="" method="post" class="form-rating-{{ word }} w-full">
2
+ {% csrf_token %}
3
+ <article class="overflow-x-scroll w-full">
4
+ <article class="bg-surface-card p-6 rounded-lg mb-3 w-fit">
5
+ <label for="id-scale-word-{{ word }}"
6
+ class="text-xl font-bold tracking-wide block mb-6 first-letter:uppercase">{{ word }}</label>
7
+ <span class="hidden id-word">{{ word.id }}</span>
8
+
9
+ <section>
10
+ <div class="w-full min-w-xs">
11
+ <div class="relative mx-6">
12
+ <input type="range" min="1" max="{{ scale.longitud }}" step="1" name="rating-word"
13
+ class="w-full range range-md text-blue-400 [--range-bg:orange] [--range-thumb:blue] [--range-fill:0] z-5">
14
+ <div class="absolute top-0 left-0 w-full h-full z-10 flex justify-between pointer-events-none">
15
+ {% for tag in tags %}
16
+ <div class="w-0.5 transform -translate-x-1/2 h-full bg-red-500">
17
+ </div>
18
+ {% endfor %}
19
+ </div>
20
+ </div>
21
+
22
+ <div class="flex justify-between mt-4 text-xs gap-1">
23
+ {% for tag in tags %}
24
+ <div class="flex items-center justify-center text-center w-[70px]">
25
+ <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
26
+ {{ tag.id_etiqueta }}
27
+ </span>
28
+ </div>
29
+ {% endfor %}
30
+ </div>
31
+ </div>
32
+ </section>
33
+ </article>
34
+ </article>
35
+
36
+ <article class="flex flex-col justify-center gap-2 items-end actions-{{ word }}">
37
+ <section class="flex justify-end items-center gap-2 btns-container">
38
+ <button type="button" onclick="checkSendRating('{{ word }}')"
39
+ class="ct-btn-check-{{ word }} cts-btn-general-compress py-2 px-4 cts-btn-secondary btn-push">
40
+ ¿Guardar calificación?
41
+ </button>
42
+ <button type="button" onclick="sendRating('{{ word }}')"
43
+ class="ct-btn-submit-{{ word }} cts-btn-general-compress py-2 px-4 cts-btn-primary btn-push hidden">
44
+ Estoy seguro
45
+ </button>
46
+ <button type="button" onclick="cancelSendRating('{{ word }}')"
47
+ class="ct-btn-cancel-{{ word }} cts-btn-general-compress py-2 px-4 cts-btn-error btn-push hidden">
48
+ Cancelar
49
+ </button>
50
+ </section>
51
+ </article>
52
+ </form>
tecnicas/templates/tecnicas/components/item_session_tester.html CHANGED
@@ -10,7 +10,7 @@
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>
 
10
 
11
  <article class="text-sm space-y-1">
12
  <p><span class="font-semibold">Código:</span> {{ session.codigo_sesion }}</p>
13
+ <p class="uppercase"><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>
tecnicas/templates/tecnicas/components/item_vocabulary.html ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class='card bg-surface-ligt shadow-lg text-black sm:max-w-80 w-full flex justify-center items-center'>
2
+ <div class="card-body flex flex-col justify-between w-full">
3
+ <div class="flex flex-col gap-2">
4
+ <h2 class="text-xl font-bold text-right bg-btn-secondary text-white p-2 rounded">
5
+ {{ vocabulary.nombre_vocabulario }}
6
+ </h2>
7
+ <div class="text-lg bg-surface-alter-card p-2 rounded">
8
+ <p class="font-bold">Creado en:</p>
9
+ <p>{{ vocabulary.creado }}</p>
10
+ </div>
11
+ </div>
12
+ <div>
13
+ <a href="{% url 'cata_system:crear_vocabulario' %}?name_voca={{vocabulary.nombre_vocabulario}}">
14
+ <button class="btn bg-btn-primary border-0 block w-full btn-push">
15
+ Revisar Vocabulario
16
+ </button>
17
+ </a>
18
+ </div>
19
+ </div>
20
+ </div>
tecnicas/templates/tecnicas/components/table-convencional.html CHANGED
@@ -1,10 +1,10 @@
 
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 %}
@@ -13,34 +13,40 @@
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
- {{ valor.dato_valor }}
27
- {% with match=True %}{% endwith %}
28
- {% endif %}
 
 
 
 
 
 
 
 
29
  {% endfor %}
30
- {% if not match %}0{% endif %}
31
- {% endwith %}
32
- </td>
33
  {% endfor %}
34
- </tr>
35
- {% endfor %}
36
  {% endfor %}
37
  </tbody>
38
  </table>
39
  </div>
40
 
41
  <div class="flex justify-end mt-3">
42
- <button class="cts-btn-general cts-btn-primary btn-push">
 
 
43
  Descargar CSV
44
  </button>
45
  </div>
46
- </section>
 
 
1
+ {% load static %}
2
  <section>
 
 
3
  <div class="overflow-x-auto rounded-lg border border-surface-general">
4
+ <table id="convencional-table" class="min-w-max w-full text-sm text-center border-collapse">
5
  <thead class="bg-surface-sweet text-black font-semibold">
6
  <tr>
7
+ <th class="py-2 px-3 border border-surface-general">Repetición</th>
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 %}
 
13
  </tr>
14
  </thead>
15
  <tbody class="bg-surface-ligt divide-y divide-gray-200">
16
+ {% for repeticion, catadores in calificaciones.items %}
17
+ {% for usuario, productos in catadores.items %}
18
+ {% for codigo, valores in productos.items %}
19
+ <tr>
20
+ <td class="py-2 px-3 border border-surface-general">{{ repeticion }}</td>
21
+ <td class="py-2 px-3 border border-surface-general">{{ usuario }}</td>
22
+ <td class="py-2 px-3 border border-surface-general">{{ codigo }}</td>
23
+ {% for palabra in palabras %}
24
+ <td class="py-2 px-3 border border-surface-general">
25
+ {% with match=False %}
26
+ {% for valor in valores %}
27
+ {% if valor.nombre_palabra == palabra %}
28
+ {{ valor.dato_valor }}
29
+ {% with match=True %}{% endwith %}
30
+ {% endif %}
31
+ {% endfor %}
32
+ {% if match %}0{% endif %}
33
+ {% endwith %}
34
+ </td>
35
+ {% endfor %}
36
+ </tr>
37
  {% endfor %}
 
 
 
38
  {% endfor %}
 
 
39
  {% endfor %}
40
  </tbody>
41
  </table>
42
  </div>
43
 
44
  <div class="flex justify-end mt-3">
45
+ <button id="download-csv-btn" class="cts-btn-general cts-btn-primary btn-push"
46
+ data-session-name="{% if sesion and sesion.nombre_sesion %}{{ sesion.nombre_sesion }}{% else %}{% endif %}"
47
+ data-session-code="{% if sesion and sesion.codigo_sesion %}{{ sesion.codigo_sesion }}{% else %}{% endif %}">
48
  Descargar CSV
49
  </button>
50
  </div>
51
+ </section>
52
+ <script src="{% static 'js/download-table-csv.js' %}"></script>
tecnicas/templates/tecnicas/create_sesion/conf-panel-vocabulary.html ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'tecnicas/layouts/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block content %}
5
+ <article class="cts-container-main">
6
+ <article class="cts-wrap-content text-black lg:w-4xl">
7
+ <h2 class="text-2xl font-bold text-center bg-surface-card p-4 rounded-lg">
8
+ Elegir vocabulario
9
+ </h2>
10
+
11
+ <form method="get" id="form-vocabulario" class="bg-surface-card rounded-lg p-6">
12
+ <label for="{{ form.vocabulario.id_for_label }}"
13
+ class="text-lg w-fit font-medium tracking-wide w-full space-y-4">
14
+ <p class="tracking-normal text-xl font-bold">Seleccione un vocabulario</p>
15
+ {{ form.vocabulario }}
16
+ </label>
17
+ </form>
18
+
19
+ {% if error %}
20
+ {% include "../components/error-message.html" with message=error %}
21
+ {% endif %}
22
+ {% if message %}
23
+ {% include "../components/error-message.html" with message=message %}
24
+ {% endif %}
25
+
26
+ <div id="palabras-container" class="rounded-lg p-3 min-h-24 max-h-64 overflow-y-auto bg-surface-card">
27
+ <h3 class="text-xl font-semibold">Palabras asociadas:</h3>
28
+ <ul id="palabras-lista" class="flex flex-wrap gap-4">
29
+ <li class="text-center text-lg">Selecciona un vocabulario para ver sus palabras</li>
30
+ </ul>
31
+ </div>
32
+
33
+ <section>
34
+ <form action="" method="post" id="cts-create-session" class="flex justify-center mt-4">
35
+ {% csrf_token %}
36
+ <article class="cs-escalas-radio w-full flex flex-col gap-4">
37
+ <section class="flex max-sm:flex-col flex-wrap items-center justify-center gap-4">
38
+ <button type="submit" name="start"
39
+ class="cts-btn-general cts-btn-primary btn-push flex-1 w-full">
40
+ Crear sesión
41
+ </button>
42
+ <a href="{% url 'cata_system:seleccion_tecnica' %}" class="flex-1 w-full">
43
+ <button type="button" class="cts-btn-general cts-btn-secondary btn-push w-full">
44
+ Paso anterior
45
+ </button>
46
+ </a>
47
+ </section>
48
+ <a href="{% url 'cata_system:seleccion_tecnica' %}" class="flex-1 w-full">
49
+ <button type="button" class="cts-btn-general cts-btn-error btn-push w-full">
50
+ Cancelar la creación
51
+ </button>
52
+ </a>
53
+ </article>
54
+ </form>
55
+ </section>
56
+ </article>
57
+ </article>
58
+ {% endblock %}
59
+
60
+ {% block extra_js %}
61
+ <script src="{% static 'js/choose-vocabulary.js' %}"></script>
62
+ {% endblock %}
tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html CHANGED
@@ -7,13 +7,18 @@
7
  <article class="cts-container-main">
8
  <article class="cts-wrap-content text-black">
9
  <h1 class="text-center font-bold text-4xl">Panel de configuración</h1>
 
10
  {% if error %}
11
- <p class="text-2xl">{{ error }}</p>
 
 
 
12
  {% endif %}
 
13
  <form method="post" action="" class="ct-codes-form flex flex-col gap-5">
14
  {% csrf_token %}
15
  <h2 class="text-2xl mb-2 font-bold">Codificar productos</h2>
16
- <hr>
17
  <article class="w-full flex flex-col justify-center items-center gap-5">
18
  <p class="text-center font-bold tracking-wide text-xl bg-surface-card px-3 py-3 pb-4 rounded max-w-4xl">
19
  Puede dejar los campos como están para usar los códigos predefinidos.
@@ -47,7 +52,6 @@
47
  </article>
48
  {% endif %}
49
 
50
- <hr>
51
  <article class="flex flex-col items-center gap-4">
52
  <section class="flex w-full max-sm:flex-col flex-wrap gap-4 max-w-2xl">
53
  <button type="submit" class="cts-btn-general cts-btn-primary btn-push flex-1 w-full">
 
7
  <article class="cts-container-main">
8
  <article class="cts-wrap-content text-black">
9
  <h1 class="text-center font-bold text-4xl">Panel de configuración</h1>
10
+
11
  {% if error %}
12
+ {% include "./components/error-message.html" with message=error %}
13
+ {% endif %}
14
+ {% if message %}
15
+ {% include "./components/error-message.html" with message=message %}
16
  {% endif %}
17
+
18
  <form method="post" action="" class="ct-codes-form flex flex-col gap-5">
19
  {% csrf_token %}
20
  <h2 class="text-2xl mb-2 font-bold">Codificar productos</h2>
21
+
22
  <article class="w-full flex flex-col justify-center items-center gap-5">
23
  <p class="text-center font-bold tracking-wide text-xl bg-surface-card px-3 py-3 pb-4 rounded max-w-4xl">
24
  Puede dejar los campos como están para usar los códigos predefinidos.
 
52
  </article>
53
  {% endif %}
54
 
 
55
  <article class="flex flex-col items-center gap-4">
56
  <section class="flex w-full max-sm:flex-col flex-wrap gap-4 max-w-2xl">
57
  <button type="submit" class="cts-btn-general cts-btn-primary btn-push flex-1 w-full">
tecnicas/templates/tecnicas/create_sesion/panel-basic-cata.html ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'tecnicas/layouts/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title %}Panel Configuracion{% endblock %}
5
+
6
+ {% block content %}
7
+ <article class="cts-container-main">
8
+ <article class="cts-wrap-content text-black">
9
+ <h1 class="text-center font-bold text-4xl">Panel de configuración</h1>
10
+
11
+ {% if error %}
12
+ {% include "../components/error-message.html" with message=error %}
13
+ {% endif %}
14
+ {% if message %}
15
+ {% include "../components/error-message.html" with message=message %}
16
+ {% endif %}
17
+
18
+ <form method="post" action="" class="space-y-4">
19
+ {% csrf_token %}
20
+ <article class="space-y-4">
21
+ <h2 class="text-2xl font-bold">Información Basica</h2>
22
+
23
+ <section class="flex justify-center items-center">
24
+ <label for="{{form_sesion.nombre_sesion.id_for_label}}"
25
+ class="text-lg flex flex-col items-center px-2 w-fit font-medium tracking-wide mb-4">
26
+ <p class="tracking-normal text-xl font-bold">Ingrese el nombre de la sesion si lo desea:</p>
27
+ {{ form_sesion.nombre_sesion }}
28
+ </label>
29
+ </section>
30
+
31
+ <section class="flex justify-center items-center">
32
+ <label for="{{ form_sesion.numero_productos.id_for_label }}"
33
+ class="text-lg flex flex-col items-center px-2 font-medium tracking-wide">
34
+ <p class="tracking-normal text-base font-bold">
35
+ Número de Productos:
36
+ </p>
37
+ {{ form_sesion.numero_productos }}
38
+ </label>
39
+ </section>
40
+
41
+ <section class="flex justify-center items-center">
42
+ <label for="{{form_sesion.instrucciones.id_for_label}}"
43
+ class="text-lg flex flex-col items-center px-2 w-fit font-medium tracking-wide mb-4">
44
+ <p class="tracking-normal text-base font-bold">Ingrese las instrucciones para la tecnica:</p>
45
+ {{ form_sesion.instrucciones }}
46
+ </label>
47
+ </section>
48
+ </article>
49
+
50
+ <article class="cs-escalas-radio flex flex-col gap-4">
51
+ <h2 class="text-2xl font-bold">Selecciona estilo para las palabras</h2>
52
+ <section class="flex flex-row gap-4 justify-around flex-wrap">
53
+ {% for estilo in form_sesion.estilo_palabras %}
54
+ <label for="{{estilo.id_for_label}}"
55
+ class="ct-radio-estilo uppercase text-lg tracking-wider font-bold p-2 px-4 rounded-xl cts-btn-secondary btn-push">
56
+ {{ estilo.tag }}
57
+ {{ estilo.choice_label }}
58
+ </label>
59
+ {% endfor %}
60
+ </section>
61
+ <section class="w-full flex justify-center items-center">
62
+ <p
63
+ class="cts-help-style-words max-w-2xl text-lg font-medium tracking-wide bg-surface-card text-center py-1 px-4 rounded-xl">
64
+ </p>
65
+ </section>
66
+ </article>
67
+
68
+ <article class="w-full flex max-sm:flex-col flex-wrap items-center justify-center gap-4">
69
+ <button type="submit" class="cts-btn-general cts-btn-primary btn-push flex-1/4 w-full">
70
+ Continuar
71
+ </button>
72
+ <a href="{% url 'cata_system:seleccion_tecnica' %}" class="flex-1/4 w-full">
73
+ <button type="button" class="cts-btn-general cts-btn-error btn-push w-full">
74
+ Cancelar la creación
75
+ </button>
76
+ </a>
77
+ </article>
78
+ </form>
79
+ </article>
80
+ </article>
81
+ {% endblock %}
82
+
83
+ {% block extra_js %}
84
+ <script src="{% static 'js/panel-basic-cata.js' %}"></script>
85
+ {% endblock %}
tecnicas/templates/tecnicas/forms_tester/convencional.html CHANGED
@@ -37,7 +37,8 @@
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
@@ -45,12 +46,17 @@
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>
52
  </article>
53
 
 
 
 
 
54
  {% if error %}
55
  <hr>
56
  <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
@@ -95,132 +101,17 @@
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
116
- class="absolute top-0 left-0 w-0.5 transform -translate-x-1/2 h-full bg-red-500 z-10">
117
- </div>
118
-
119
- <div
120
- class="absolute top-1/5 left-1/2 w-0.5 transform -translate-x-1/2 h-4/5 bg-red-500 z-10">
121
- </div>
122
-
123
- <div
124
- class="absolute top-0 right-0 w-0.5 transform -translate-x-1/2 h-full bg-red-500 z-10">
125
- </div>
126
- </div>
127
-
128
- <div class="flex justify-between mt-4">
129
- <div class="flex flex-col items-start text-left w-[100px]">
130
- <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
131
- {{ tags.0.id_etiqueta }}
132
- </span>
133
- </div>
134
- <div class="flex flex-col items-start text-center w-[100px]">
135
- <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
136
- {{ tags.1.id_etiqueta }}
137
- </span>
138
- </div>
139
- <div class="flex flex-col items-start text-right w-[100px]">
140
- <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
141
- {{ tags.2.id_etiqueta }}
142
- </span>
143
- </div>
144
- </div>
145
- </section>
146
- </article>
147
- </article>
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>
220
- </article>
221
- </form>
222
- {% endfor %}
223
- {% endif %}
224
  </article>
225
  </article>
226
  </article>
 
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
41
+ <span class="uppercase">{{ session.tecnica.tipo_tecnica }}</span>
42
  </h1>
43
  <button class="cts-btn-general cts-btn-error btn-push" onclick="exit_sesion('form-actions')">
44
  Salir de la sesión
 
46
  </header>
47
 
48
  <article class="hidden">
49
+ <form action="{% url 'cata_system:catador_init_session' code_sesion=session.codigo_sesion %}" method="post"
50
+ class="form-actions">
51
  {% csrf_token %}
52
  <input type="hidden" name="action" class="action-input">
53
  </form>
54
  </article>
55
 
56
+ <section class="hidden">
57
+ <input type="hidden" value="{{ session.tecnica.id }}" name="id-tecnica" class="ct-input-id-tech">
58
+ </section>
59
+
60
  {% if error %}
61
  <hr>
62
  <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
 
101
 
102
  <article
103
  class="scales-container [&>*:not(:last-child)]:mb-5 min-lg:grid min-lg:items-start grid-cols-2 gap-3 justify-center items-center">
104
+ {% with path_con="../components/form-scale-continue.html" path_str="../components/form-scale-structure.html" %}
105
+ {% if type_scale == "continua" %}
106
+ {% for word in words %}
107
+ {% include path_con with word=word tags=tags scale=scale id_tecnica=session.tecnica.id %}
108
+ {% endfor %}
109
+ {% elif type_scale == "estructurada" %}
110
+ {% for word in words %}
111
+ {% include path_str with word=word tags=tags scale=scale %}
112
+ {% endfor %}
113
+ {% endif %}
114
+ {% endwith %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  </article>
116
  </article>
117
  </article>