Norberto Montalvo García commited on
Commit
893eacf
·
unverified ·
2 Parent(s): 887ec02 16f734b

Merge pull request #5 from CascoArcilla/HU9

Browse files

Muestra de palabras por producto para calificar del lado del Catador

Files changed (29) hide show
  1. tecnicas/controllers/__init__.py +17 -12
  2. tecnicas/controllers/calificacion_controller.py +0 -19
  3. tecnicas/controllers/models_controller/calificacion_controller.py +101 -0
  4. tecnicas/controllers/models_controller/catador_controller.py +10 -0
  5. tecnicas/controllers/models_controller/dato_controller.py +16 -0
  6. tecnicas/controllers/{escala_controller.py → models_controller/escala_controller.py} +30 -2
  7. tecnicas/controllers/{estilo_palabras_controller.py → models_controller/estilo_palabras_controller.py} +2 -2
  8. tecnicas/controllers/{ordenes_controller.py → models_controller/ordenes_controller.py} +10 -2
  9. tecnicas/controllers/models_controller/palabras_controller.py +47 -0
  10. tecnicas/controllers/models_controller/particiapacion_controller.py +36 -0
  11. tecnicas/controllers/models_controller/posicion_controller.py +20 -0
  12. tecnicas/controllers/{presentador_controller.py → models_controller/presentador_controller.py} +2 -2
  13. tecnicas/controllers/{productos_controller.py → models_controller/productos_controller.py} +16 -2
  14. tecnicas/controllers/{sesion_controller.py → models_controller/sesion_controller.py} +10 -2
  15. tecnicas/controllers/{tecnica_controller.py → models_controller/tecnica_controller.py} +2 -2
  16. tecnicas/controllers/palabras_controller.py +0 -31
  17. tecnicas/controllers/{detalles_sesion_controller.py → views_controller/detalles_sesion_controller.py} +3 -3
  18. tecnicas/controllers/{login_tester_controller.py → views_controller/login_tester_controller.py} +3 -3
  19. tecnicas/controllers/{main_tester_form_controller.py → views_controller/main_tester_form_controller.py} +41 -6
  20. tecnicas/static/js/created-scale.js +207 -0
  21. tecnicas/templates/tecnicas/forms_tester/convencional.html +182 -0
  22. tecnicas/templates/tecnicas/forms_tester/main_tester.html +17 -0
  23. tecnicas/templates/tecnicas/layouts/base.html +1 -1
  24. tecnicas/urls.py +8 -0
  25. tecnicas/views/__init__.py +2 -0
  26. tecnicas/views/apis/rating_word.py +17 -0
  27. tecnicas/views/login_tester.py +4 -0
  28. tecnicas/views/tester_forms/convencional_scales.py +129 -0
  29. tecnicas/views/tester_forms/main_tester_form.py +30 -13
tecnicas/controllers/__init__.py CHANGED
@@ -1,12 +1,17 @@
1
- from .tecnica_controller import TecnicaController
2
- from .escala_controller import EscalaController
3
- from .productos_controller import ProductosController
4
- from .ordenes_controller import OrdenesController
5
- from .palabras_controller import PalabrasController
6
- from .estilo_palabras_controller import EstiloPalabrasController
7
- from .palabras_controller import PalabrasController
8
- from .sesion_controller import SesionController
9
- from .calificacion_controller import CalificacionController
10
- from .detalles_sesion_controller import DetallesSesionController
11
- from .login_tester_controller import LoginTesterController
12
- from .main_tester_form_controller import MainTesterFormController
 
 
 
 
 
 
1
+ from .models_controller.tecnica_controller import TecnicaController
2
+ from .models_controller.escala_controller import EscalaController
3
+ from .models_controller.productos_controller import ProductosController
4
+ from .models_controller.ordenes_controller import OrdenesController
5
+ from .models_controller.palabras_controller import PalabrasController
6
+ from .models_controller.estilo_palabras_controller import EstiloPalabrasController
7
+ from .models_controller.palabras_controller import PalabrasController
8
+ from .models_controller.sesion_controller import SesionController
9
+ from .models_controller.calificacion_controller import CalificacionController
10
+ from .models_controller.catador_controller import CatadorController
11
+ from .models_controller.posicion_controller import PosicionController
12
+ from .models_controller.particiapacion_controller import ParticipacionController
13
+ from .models_controller.dato_controller import DatoController
14
+
15
+ from .views_controller.detalles_sesion_controller import DetallesSesionController
16
+ from .views_controller.login_tester_controller import LoginTesterController
17
+ from .views_controller.main_tester_form_controller import MainTesterFormController
tecnicas/controllers/calificacion_controller.py DELETED
@@ -1,19 +0,0 @@
1
- from ..models import Calificacion, Tecnica
2
-
3
-
4
- class CalificacionController():
5
- @staticmethod
6
- def getRatingsByTechnique(technique: Tecnica):
7
- repetition = technique.repecion
8
-
9
- if not repetition:
10
- return {"error": "sin datos calficados aun"}
11
-
12
- data_rating = {}
13
-
14
- for i in range(repetition):
15
- response_data = Calificacion.objects.filter(
16
- id_tecnica=technique, num_repeticion=i+1)
17
- data_rating[f"repeticion_{i+1}"] = response_data
18
-
19
- return data_rating
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/controllers/models_controller/calificacion_controller.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Calificacion, Tecnica, Posicion, Producto, Catador
2
+ from ...utils import controller_error
3
+ from collections import defaultdict
4
+
5
+
6
+ class CalificacionController():
7
+ @staticmethod
8
+ def getRatingsByTechnique(technique: Tecnica):
9
+ repetition = technique.repecion
10
+
11
+ if not repetition:
12
+ return {"error": "sin datos calficados aun"}
13
+
14
+ data_rating = {}
15
+
16
+ for i in range(repetition):
17
+ response_data = Calificacion.objects.filter(
18
+ id_tecnica=technique, num_repeticion=i+1)
19
+ data_rating[f"repeticion_{i+1}"] = response_data
20
+
21
+ return data_rating
22
+
23
+ @staticmethod
24
+ def getRatings(
25
+ technique: Tecnica = None,
26
+ id_technique: int = None,
27
+ repetition: int = None,
28
+ product: Producto = None,
29
+ id_product: int = None,
30
+ tester: Catador = None,
31
+ id_tester: int = None,
32
+ user_tester: str = None,):
33
+ if repetition is None:
34
+ return controller_error("Es necesario especificar la repetición")
35
+
36
+ filters = {"num_repeticion": repetition}
37
+
38
+ if technique is not None:
39
+ filters["id_tecnica"] = technique
40
+ elif id_technique is not None:
41
+ filters["id_tecnica__id"] = id_technique
42
+ else:
43
+ return controller_error("Es necesario especificar la técnica")
44
+
45
+ if product is not None:
46
+ filters["id_producto"] = product
47
+ elif id_product is not None:
48
+ filters["id_producto__id"] = id_product
49
+
50
+ if tester is not None:
51
+ filters["id_catador"] = tester
52
+ elif id_tester is not None:
53
+ filters["id_catador__id"] = id_tester
54
+ elif user_tester is not None:
55
+ filters["id_catador__usuarioCatador"] = user_tester
56
+
57
+ ratings = list(Calificacion.objects.filter(**filters).select_related(
58
+ "id_producto",
59
+ "id_tecnica",
60
+ "id_catador",
61
+ ))
62
+
63
+ return ratings
64
+
65
+ @staticmethod
66
+ def checkProducsWithoutRating(
67
+ positions: list[Posicion] = None,
68
+ user_cata: str = None,
69
+ repetition: int = None,
70
+ technique: Tecnica = None,
71
+ id_technique: int = None,
72
+ num_words: int = None):
73
+ check_products = [position.id_producto for position in positions]
74
+
75
+ filters = {
76
+ "user_tester": user_cata,
77
+ "repetition": repetition
78
+ }
79
+
80
+ if technique is not None:
81
+ filters["technique"] = technique
82
+ elif id_technique is not None:
83
+ filters["id_technique"] = id_technique
84
+
85
+ ratings = CalificacionController.getRatings(**filters)
86
+
87
+ if len(ratings) == 0:
88
+ return positions
89
+
90
+ ratings_dict = defaultdict(list)
91
+
92
+ for rat in ratings:
93
+ ratings_dict[rat.id_producto.id].append(rat)
94
+
95
+ for index, product in enumerate(check_products):
96
+ rating_of_product = ratings_dict.get(product.id, [])
97
+
98
+ if rating_of_product < num_words or len(rating_of_product) == 0:
99
+ return positions[index]
100
+
101
+ return controller_error("Sin productos por calificar")
tecnicas/controllers/models_controller/catador_controller.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Catador
2
+ from ...utils import controller_error
3
+
4
+ class CatadorController:
5
+ def getTesterByUsername(username:str):
6
+ try:
7
+ tester = Catador.objects.get(usuarioCatador=username)
8
+ return tester
9
+ except Catador.DoesNotExist:
10
+ return controller_error("No se encontró el Catador")
tecnicas/controllers/models_controller/dato_controller.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Calificacion, Dato
2
+ from ...utils import controller_error
3
+
4
+
5
+ class DatoController():
6
+ @staticmethod
7
+ def getRerecordedData(ratings: list[Calificacion]):
8
+ if not ratings:
9
+ return []
10
+
11
+ ids_ratings = [rat.id for rat in ratings]
12
+
13
+ recoreded_data = list(Dato.objects.filter(
14
+ id_calificacion_id__in=ids_ratings))
15
+
16
+ return recoreded_data
tecnicas/controllers/{escala_controller.py → models_controller/escala_controller.py} RENAMED
@@ -1,6 +1,6 @@
1
- from ..models import Etiqueta, EtiquetasEscala, Escala, TipoEscala
2
  from django.db import DatabaseError
3
- from ..utils import controller_error
4
 
5
 
6
  class EscalaController():
@@ -68,3 +68,31 @@ class EscalaController():
68
  except DatabaseError as error:
69
  self.deleteRelationshipsWithLabels()
70
  return controller_error("error guardar relacion etiqueta escala")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Etiqueta, EtiquetasEscala, Escala, TipoEscala, Tecnica
2
  from django.db import DatabaseError
3
+ from ...utils import controller_error
4
 
5
 
6
  class EscalaController():
 
68
  except DatabaseError as error:
69
  self.deleteRelationshipsWithLabels()
70
  return controller_error("error guardar relacion etiqueta escala")
71
+
72
+ @staticmethod
73
+ def getScaleByTechnique(technique: Tecnica = None, id_technique: int = None):
74
+ if technique is not None:
75
+ scale = Escala.objects.select_related("id_tipo_escala").only(
76
+ "id_tipo_escala", "longitud").get(tecnica=technique)
77
+ return scale
78
+
79
+ if id_technique is not None:
80
+ scale = Escala.objects.select_related("id_tipo_escala").only(
81
+ "id_tipo_escala", "longitud").get(tecnica_id=id_technique)
82
+ return scale
83
+
84
+ @staticmethod
85
+ def getRelatedTagsInScale(scale: Escala = None, id_scale: int = None):
86
+ if scale is not None:
87
+ scale_tags = list(EtiquetasEscala.objects.filter(
88
+ id_escala=scale).select_related("id_etiqueta"))
89
+ if len(scale_tags) == 0:
90
+ return controller_error("Imposible obtener las etiquetas")
91
+ return scale_tags
92
+
93
+ if id_scale is not None:
94
+ scale_tags = list(EtiquetasEscala.objects.filter(
95
+ id_escala_id=scale).select_related("id_etiqueta"))
96
+ if len(scale_tags) == 0:
97
+ return controller_error("Imposible obtener las etiquetas")
98
+ return scale_tags
tecnicas/controllers/{estilo_palabras_controller.py → models_controller/estilo_palabras_controller.py} RENAMED
@@ -1,6 +1,6 @@
1
- from ..models import EsAtributo, Tecnica, Palabra
2
  from django.db import DatabaseError
3
- from ..utils import controller_error
4
 
5
 
6
  class EstiloPalabrasController():
 
1
+ from ...models import EsAtributo, Tecnica, Palabra
2
  from django.db import DatabaseError
3
+ from ...utils import controller_error
4
 
5
 
6
  class EstiloPalabrasController():
tecnicas/controllers/{ordenes_controller.py → models_controller/ordenes_controller.py} RENAMED
@@ -1,5 +1,5 @@
1
- from ..models import Orden, Posicion, Producto, Tecnica
2
- from ..utils import controller_error
3
  from django.db import DatabaseError
4
 
5
 
@@ -74,3 +74,11 @@ class OrdenesController():
74
  return self.positions
75
  except DatabaseError as error:
76
  return controller_error("error al guardar las posiciones")
 
 
 
 
 
 
 
 
 
1
+ from ...models import Orden, Posicion, Producto, Tecnica
2
+ from ...utils import controller_error
3
  from django.db import DatabaseError
4
 
5
 
 
74
  return self.positions
75
  except DatabaseError as error:
76
  return controller_error("error al guardar las posiciones")
77
+
78
+ @staticmethod
79
+ def getOrderById(id: int):
80
+ try:
81
+ order = Orden.objects.get(id=id)
82
+ return order
83
+ except Orden.DoesNotExist:
84
+ return controller_error("No existe orden")
tecnicas/controllers/models_controller/palabras_controller.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Palabra, Tecnica, EsAtributo, EsVocabulario, Dato
2
+ from django.db import DatabaseError
3
+ from ...utils import controller_error
4
+
5
+
6
+ class PalabrasController():
7
+ ids_words: list[int]
8
+ words: list[Palabra]
9
+
10
+ def __init__(self, ids: list[int]):
11
+ self.ids_words = ids
12
+
13
+ def setIdsWords(self, new_ids: list[int]):
14
+ self.ids_words = new_ids
15
+
16
+ def setWords(self):
17
+ self.words = []
18
+ searched_words = list(Palabra.objects.filter(id__in=self.ids_words))
19
+ if not len(searched_words):
20
+ return controller_error("no se han encontrado registros")
21
+ return self.words
22
+
23
+ @staticmethod
24
+ def getWordsInTechnique(technique: Tecnica):
25
+ if technique.id_estilo.nombre_estilo == "atributos":
26
+ es_atribute = EsAtributo.objects.get(id_tecnica=technique)
27
+ words = list(es_atribute.palabras.all())
28
+ return words
29
+ elif technique.id_estilo.nombre_estilo == "vocabulario":
30
+ try:
31
+ palabras = Palabra.objects.filter(
32
+ vocabulario__esvocabulario__id_tecnica=technique
33
+ )
34
+ return list(palabras.distinct()) if palabras.exists() else controller_error("Técnica sin palabras con vocabulario")
35
+ except Exception as e:
36
+ return controller_error("Técnica sin palabras con vocabulario")
37
+
38
+ @staticmethod
39
+ def getWordsWithoutData(recoreded_data: list[Dato], words: list[Palabra]):
40
+ qualified_words = [data.id_palabra for data in recoreded_data]
41
+
42
+ if set(qualified_words) == set(words):
43
+ return controller_error("No hay palabras por usar")
44
+
45
+ words_without_use = list(set(words).difference(set(qualified_words)))
46
+
47
+ return words_without_use
tecnicas/controllers/models_controller/particiapacion_controller.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Participacion
2
+ from ...utils import controller_error
3
+
4
+
5
+ class ParticipacionController():
6
+ @staticmethod
7
+ def enterSession(id_participation: int):
8
+ try:
9
+ participation = Participacion.objects.get(id=id_participation)
10
+ participation.finalizado = False
11
+ participation.activo = True
12
+ participation.save()
13
+ return participation
14
+ except Participacion.DoesNotExist:
15
+ return controller_error("No se ha encontrado la participación")
16
+
17
+ @staticmethod
18
+ def finishSession(id_participation: int):
19
+ try:
20
+ participation = Participacion.objects.get(id=id_participation)
21
+ participation.finalizado = True
22
+ participation.activo = False
23
+ participation.save()
24
+ return participation
25
+ except Participacion.DoesNotExist:
26
+ return controller_error("No se ha encontrado la participación")
27
+
28
+ @staticmethod
29
+ def outSession(id_participation: int):
30
+ try:
31
+ participation = Participacion.objects.get(id=id_participation)
32
+ participation.activo = False
33
+ participation.save()
34
+ return participation
35
+ except Participacion.DoesNotExist:
36
+ return controller_error("No se ha encontrado la participación")
tecnicas/controllers/models_controller/posicion_controller.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...utils import controller_error
2
+ from ...models import Orden, Posicion
3
+
4
+
5
+ class PosicionController():
6
+ @staticmethod
7
+ def getPostionsInOrder(order: Orden = None, id_order: id = None):
8
+ if order is not None:
9
+ positions = list(Posicion.objects.filter(id_orden=order))
10
+ if not positions:
11
+ return controller_error("No existe orden")
12
+ return positions
13
+
14
+ if id_order is not None:
15
+ positions = list(Posicion.objects.filter(id_orden_id=id_order))
16
+ if not positions:
17
+ return controller_error("No existe orden")
18
+ return positions
19
+
20
+ return controller_error("No se define búsqueda")
tecnicas/controllers/{presentador_controller.py → models_controller/presentador_controller.py} RENAMED
@@ -1,5 +1,5 @@
1
- from ..models import Presentador
2
- from ..utils import controller_error
3
 
4
 
5
  class PresentadorController():
 
1
+ from ...models import Presentador
2
+ from ...utils import controller_error
3
 
4
 
5
  class PresentadorController():
tecnicas/controllers/{productos_controller.py → models_controller/productos_controller.py} RENAMED
@@ -1,6 +1,6 @@
1
  from django.db import DatabaseError
2
- from ..utils import controller_error
3
- from ..models import Producto, Tecnica
4
 
5
 
6
  class ProductosController():
@@ -37,3 +37,17 @@ class ProductosController():
37
  return self.list_product
38
  except DatabaseError as error:
39
  return controller_error("error al guardar los productos")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.db import DatabaseError
2
+ from ...utils import controller_error
3
+ from ...models import Producto, Tecnica
4
 
5
 
6
  class ProductosController():
 
37
  return self.list_product
38
  except DatabaseError as error:
39
  return controller_error("error al guardar los productos")
40
+
41
+ @staticmethod
42
+ def getProductsByTechniqe(technique: Tecnica = None, id_technique: int = None):
43
+ if technique is not None:
44
+ products = list(Producto.objects.filter(id_tecnica=technique))
45
+ if not products:
46
+ return controller_error("No existen productos en esta técnica")
47
+ return products
48
+
49
+ if id_technique is not None:
50
+ products = list(Producto.objects.filter(id_tecnica_id=id_technique))
51
+ if not products:
52
+ return controller_error("No existen productos en esta técnica")
53
+ return products
tecnicas/controllers/{sesion_controller.py → models_controller/sesion_controller.py} RENAMED
@@ -1,7 +1,7 @@
1
  from django.db import DatabaseError
2
  from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
3
- from ..models import Tecnica, Presentador, SesionSensorial
4
- from ..utils import controller_error
5
 
6
 
7
  class SesionController():
@@ -91,6 +91,14 @@ class SesionController():
91
  return session
92
  except SesionSensorial.DoesNotExist:
93
  return controller_error("La sesión ya no existe")
 
 
 
 
 
 
 
 
94
 
95
  @staticmethod
96
  def getNumberSessionsByCreator(user_name: str):
 
1
  from django.db import DatabaseError
2
  from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
3
+ from ...models import Tecnica, Presentador, SesionSensorial
4
+ from ...utils import controller_error
5
 
6
 
7
  class SesionController():
 
91
  return session
92
  except SesionSensorial.DoesNotExist:
93
  return controller_error("La sesión ya no existe")
94
+
95
+ @staticmethod
96
+ def getSessionByCode(code: str):
97
+ try:
98
+ session = SesionSensorial.objects.get(codigo_sesion=code)
99
+ return session
100
+ except SesionSensorial.DoesNotExist:
101
+ return controller_error("La sesión ya no existe")
102
 
103
  @staticmethod
104
  def getNumberSessionsByCreator(user_name: str):
tecnicas/controllers/{tecnica_controller.py → models_controller/tecnica_controller.py} RENAMED
@@ -1,6 +1,6 @@
1
- from ..models import TipoTecnica, CategoriaTecnica, Tecnica, EstiloPalabra
2
  from django.db import DatabaseError
3
- from ..utils import controller_error
4
 
5
 
6
  class TecnicaController():
 
1
+ from ...models import TipoTecnica, CategoriaTecnica, Tecnica, EstiloPalabra
2
  from django.db import DatabaseError
3
+ from ...utils import controller_error
4
 
5
 
6
  class TecnicaController():
tecnicas/controllers/palabras_controller.py DELETED
@@ -1,31 +0,0 @@
1
- from ..models import Palabra, Tecnica, EsAtributo
2
- from django.db import DatabaseError
3
- from ..utils import controller_error
4
-
5
-
6
- class PalabrasController():
7
- ids_words: list[int]
8
- words: list[Palabra]
9
-
10
- def __init__(self, ids: list[int]):
11
- self.ids_words = ids
12
-
13
- def setIdsWords(self, new_ids: list[int]):
14
- self.ids_words = new_ids
15
-
16
- def setWords(self):
17
- self.words = []
18
- try:
19
- searched_words = Palabra.objects.filter(id__in=self.ids_words)
20
- if not len(searched_words):
21
- return controller_error("no se han encontrado registros")
22
- self.words = searched_words
23
- return self.words
24
- except DatabaseError as error:
25
- return controller_error("error al guardar buscar palabras")
26
-
27
- @staticmethod
28
- def getWordsInTechnique(technique: Tecnica):
29
- es_atributo = EsAtributo.objects.get(id_tecnica=technique)
30
- words = es_atributo.palabras.all()
31
- return words
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/controllers/{detalles_sesion_controller.py → views_controller/detalles_sesion_controller.py} RENAMED
@@ -1,6 +1,6 @@
1
- from ..models import SesionSensorial, Presentador, Tecnica
2
- from . import CalificacionController, PalabrasController
3
- from ..utils import controller_error
4
 
5
 
6
  class DetallesSesionController():
 
1
+ from ...models import SesionSensorial, Presentador, Tecnica
2
+ from .. import CalificacionController, PalabrasController
3
+ from ...utils import controller_error
4
 
5
 
6
  class DetallesSesionController():
tecnicas/controllers/{login_tester_controller.py → views_controller/login_tester_controller.py} RENAMED
@@ -1,5 +1,5 @@
1
- from ..models import Catador, SesionSensorial, Participacion
2
- from ..utils import controller_error
3
  from django.db import transaction
4
 
5
 
@@ -18,7 +18,7 @@ class LoginTesterController():
18
  self.session = SesionSensorial.objects.get(
19
  codigo_sesion=code_session)
20
 
21
- return True
22
  except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
23
  return controller_error("Credenciales inválidas")
24
 
 
1
+ from ...models import Catador, SesionSensorial, Participacion
2
+ from ...utils import controller_error
3
  from django.db import transaction
4
 
5
 
 
18
  self.session = SesionSensorial.objects.get(
19
  codigo_sesion=code_session)
20
 
21
+ return (self.tester, self.session)
22
  except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
23
  return controller_error("Credenciales inválidas")
24
 
tecnicas/controllers/{main_tester_form_controller.py → views_controller/main_tester_form_controller.py} RENAMED
@@ -1,5 +1,5 @@
1
- from ..models import Catador, SesionSensorial, Orden
2
- from ..utils import controller_error, shuffleArray
3
  from django.db import transaction
4
 
5
 
@@ -18,10 +18,10 @@ class MainTesterFormController():
18
 
19
  def assignOrder(self):
20
  with transaction.atomic():
21
- orders_without_tester = Orden.objects.select_for_update().filter(
22
- id_tecnica=self.session.tecnica, id_catador=None)
23
 
24
- if not len(orders_without_tester):
25
  return controller_error("Las ordenes se han acabado")
26
 
27
  shuffle_orders = shuffleArray(orders_without_tester)
@@ -35,7 +35,7 @@ class MainTesterFormController():
35
  def checkAssignOrder(self):
36
  if not self.tester or not self.session:
37
  return controller_error("Atributos no establecidos")
38
-
39
  try:
40
  res_order = Orden.objects.get(
41
  id_tecnica=self.session.tecnica, id_catador=self.tester)
@@ -43,3 +43,38 @@ class MainTesterFormController():
43
  return self.order
44
  except Orden.DoesNotExist:
45
  return controller_error("Catador sin orden")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ...models import Catador, SesionSensorial, Orden, Participacion, Producto, EsAtributo, Calificacion, EsVocabulario
2
+ from ...utils import controller_error, shuffleArray
3
  from django.db import transaction
4
 
5
 
 
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
+ if not orders_without_tester:
25
  return controller_error("Las ordenes se han acabado")
26
 
27
  shuffle_orders = shuffleArray(orders_without_tester)
 
35
  def checkAssignOrder(self):
36
  if not self.tester or not self.session:
37
  return controller_error("Atributos no establecidos")
38
+
39
  try:
40
  res_order = Orden.objects.get(
41
  id_tecnica=self.session.tecnica, id_catador=self.tester)
 
43
  return self.order
44
  except Orden.DoesNotExist:
45
  return controller_error("Catador sin orden")
46
+
47
+ def isEndedSession(self, id_participation: int, repetition: int):
48
+ if not self.order or not id_participation:
49
+ return controller_error("Se requieren datos para comprobar la finalización")
50
+
51
+ try:
52
+ participation = Participacion.objects.get(id=id_participation)
53
+
54
+ if participation.finalizado:
55
+ num_products = Producto.objects.filter(
56
+ id_tecnica=self.session.tecnica).count()
57
+
58
+ style_words = self.session.tecnica.id_estilo
59
+
60
+ num_words: int
61
+
62
+ if style_words.nombre_estilo == "atributos":
63
+ e_atribues = EsAtributo.objects.get(
64
+ id_tecnica=self.session.tecnica)
65
+ num_words = e_atribues.palabras.count()
66
+ elif style_words.nombre_estilo == "vocabulario":
67
+ e_vocabulary = EsVocabulario.objects.get(
68
+ id_tecnica=self.session.tecnica)
69
+ num_words = e_vocabulary.id_vocabulario.palabras.count()
70
+
71
+ num_ratings_now = Calificacion.objects.filter(
72
+ id_tecnica=self.session.tecnica, id_catador=self.tester, num_repeticion=repetition).count()
73
+
74
+ num_ratings_max_by_tester = num_products * num_words
75
+
76
+ return not num_ratings_now <= num_ratings_max_by_tester
77
+ else:
78
+ return participation.finalizado
79
+ except Participacion.DoesNotExist:
80
+ return controller_error("No se ha encontrado la participación")
tecnicas/static/js/created-scale.js ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const BTN_CLASS_STYLE = [
2
+ "text-lg",
3
+ "tracking-wider",
4
+ "font-medium",
5
+ "p-2",
6
+ "px-4",
7
+ "border-b-2",
8
+ "active:border-b-0",
9
+ "active:border-t-2",
10
+ "active:border-blue-500",
11
+ "border-blue-800",
12
+ "transition-all",
13
+ "rounded-xl",
14
+ "bg-blue-500",
15
+ "text-white",
16
+ "w-fit",
17
+ "disabled:bg-amber-600",
18
+ ];
19
+
20
+ function checkSendRating(word) {
21
+ const btnCheck = document.querySelector(`.ct-btn-check-${word}`);
22
+ const btnSend = document.querySelector(`.ct-btn-submit-${word}`);
23
+ const btnCancel = document.querySelector(`.ct-btn-cancel-${word}`);
24
+
25
+ btnCheck.classList.add("hidden");
26
+ btnSend.classList.remove("hidden");
27
+ btnCancel.classList.remove("hidden");
28
+ }
29
+
30
+ function cancelSendRating(word) {
31
+ const btnCheck = document.querySelector(`.ct-btn-check-${word}`);
32
+ const btnSend = document.querySelector(`.ct-btn-submit-${word}`);
33
+ const btnCancel = document.querySelector(`.ct-btn-cancel-${word}`);
34
+
35
+ btnCheck.classList.remove("hidden");
36
+ btnSend.classList.add("hidden");
37
+ btnCancel.classList.add("hidden");
38
+ }
39
+
40
+ async function sendRating(word) {
41
+ const containerBtn = document.querySelector(`.actions-${word}`);
42
+ addOrRemoveWaitSpin(containerBtn);
43
+
44
+ const formRatingWord = document.querySelector(`.form-rating-${word}`);
45
+
46
+ const dataForm = new FormData(formRatingWord);
47
+ const url = "/cata/testers/api/ratingword";
48
+
49
+ dataForm.set("name-word", word);
50
+
51
+ try {
52
+ const respone = await fetch(url, {
53
+ method: "POST",
54
+ headers: {
55
+ "X-CSRFToken": dataForm.get("csrfmiddlewaretoken"),
56
+ },
57
+ body: dataForm,
58
+ });
59
+
60
+ const jsonResponse = await respone.json();
61
+
62
+ if (jsonResponse.error) {
63
+ addOrRemoveWaitSpin(containerBtn, false, jsonResponse.error);
64
+ return;
65
+ }
66
+
67
+ remplaceForm(formRatingWord, jsonResponse.message);
68
+ if (isEndedRatingWords()) {
69
+ addBtnNextWord();
70
+ }
71
+ } catch (error) {
72
+ console.log("Error:", error);
73
+ }
74
+ }
75
+
76
+ function remplaceForm(oldForm, message) {
77
+ const articleContainer = document.createElement("article");
78
+ const thankYouMessage = document.createElement("p");
79
+ const messageResponse = document.createElement("p");
80
+
81
+ thankYouMessage.innerHTML =
82
+ "Palabra calificada,<br>gracias por la participación";
83
+ messageResponse.textContent = message;
84
+
85
+ articleContainer.classList.add(
86
+ "bg-gray-200",
87
+ "p-6",
88
+ "rounded-lg",
89
+ "mb-3",
90
+ "text-center"
91
+ );
92
+ thankYouMessage.classList.add("text-2xl", "font-bold");
93
+ messageResponse.classList.add("text-lg");
94
+
95
+ articleContainer.appendChild(thankYouMessage);
96
+ articleContainer.appendChild(messageResponse);
97
+ oldForm.replaceWith(articleContainer);
98
+ }
99
+
100
+ function addBtnNextWord() {
101
+ const thankYouMessage = document.createElement("p");
102
+ thankYouMessage.textContent = "Has finalizado con este producto";
103
+ thankYouMessage.classList.add("text-2xl", "font-bold", "mb-3");
104
+
105
+ const btnNesxtWord = document.createElement("button");
106
+ btnNesxtWord.classList.add(...BTN_CLASS_STYLE, "ct-btn-next-word");
107
+ btnNesxtWord.textContent = "Siguiente palabra";
108
+ btnNesxtWord.addEventListener("click", nextWord);
109
+
110
+ const articleContainer = document.createElement("article");
111
+ articleContainer.classList.add(
112
+ "bg-gray-200",
113
+ "p-6",
114
+ "rounded-lg",
115
+ "mb-3",
116
+ "text-center"
117
+ );
118
+
119
+ articleContainer.appendChild(thankYouMessage);
120
+ articleContainer.appendChild(btnNesxtWord);
121
+
122
+ const scalesContainer = document.querySelector(".scales-container");
123
+ scalesContainer.innerHTML = "";
124
+ scalesContainer.appendChild(articleContainer);
125
+ }
126
+
127
+ function isEndedRatingWords() {
128
+ const currentNumerWords = document.querySelectorAll(
129
+ 'form[class*="form-rating-"]'
130
+ ).length;
131
+
132
+ return !currentNumerWords;
133
+ }
134
+
135
+ /**
136
+ *
137
+ * @param {Event} e
138
+ */
139
+ function nextWord(e) {
140
+ location.reload();
141
+ }
142
+
143
+ function createSpinCharge() {
144
+ const divSpin = document.createElement("div");
145
+
146
+ divSpin.classList.add(
147
+ "w-10",
148
+ "h-10",
149
+ "rounded-full",
150
+ "border-4",
151
+ "border-t-4",
152
+ "border-gray-200",
153
+ "border-t-blue-600",
154
+ "animate-spin"
155
+ );
156
+
157
+ return divSpin;
158
+ }
159
+
160
+ /**
161
+ *
162
+ * @param {Element} containerBtn
163
+ */
164
+ function addOrRemoveWaitSpin(containerBtn, render = true, error = false) {
165
+ const actions = containerBtn.querySelector(`.btns-container`);
166
+
167
+ if (render) {
168
+ containerBtn.innerHTML = "";
169
+ const spin = createSpinCharge();
170
+ actions.classList.add("hidden");
171
+ containerBtn.appendChild(spin);
172
+ containerBtn.appendChild(actions);
173
+ containerBtn.classList.remove("items-end");
174
+ containerBtn.classList.add("items-center");
175
+ }
176
+
177
+ if (!render) {
178
+ containerBtn.innerHTML = "";
179
+
180
+ if (error) {
181
+ containerBtn.appendChild(createErrorSubmit(error));
182
+ }
183
+
184
+ containerBtn.append(actions);
185
+ actions.classList.remove("hidden");
186
+ containerBtn.classList.remove("items-center");
187
+ containerBtn.classList.add("items-end");
188
+ }
189
+ }
190
+
191
+ function createErrorSubmit(errorMessage) {
192
+ const containerError = document.createElement("div");
193
+ containerError.classList.add(
194
+ "w-full",
195
+ "p-2",
196
+ "bg-red-400",
197
+ "font-bold",
198
+ "text-center",
199
+ "rounded-lg"
200
+ );
201
+
202
+ const errorP = document.createElement("p");
203
+ errorP.textContent = errorMessage;
204
+
205
+ containerError.appendChild(errorP);
206
+ return containerError;
207
+ }
tecnicas/templates/tecnicas/forms_tester/convencional.html ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'tecnicas/layouts/base.html' %}
2
+
3
+ {% load static %}
4
+
5
+ {% block title %}Convencional{% endblock %}
6
+
7
+ {% block extra_css %}
8
+ <style>
9
+ input[type="range"]::-webkit-slider-thumb {
10
+ -webkit-appearance: none;
11
+ appearance: none;
12
+ width: 24px;
13
+ height: 24px;
14
+ border-radius: 50%;
15
+ background: var(--color-blue-500);
16
+ cursor: pointer;
17
+ }
18
+
19
+ input[type="range"]::-moz-range-thumb {
20
+ width: 24px;
21
+ height: 24px;
22
+ border-radius: 50%;
23
+ background: #3b82f6;
24
+ cursor: pointer;
25
+ }
26
+ </style>
27
+ {% endblock %}
28
+
29
+ {% block content %}
30
+ <article class="w-full flex flex-col justify-center items-center bg-gray-600 mt-10 mb-10">
31
+ <article class="flex flex-col gap-8 bg-gray-400 p-10 rounded">
32
+ <header class="text-center flex-row w-full flex justify-around items-center flex-wrap gap-10">
33
+ <h1 class="text-white rounded font-bold text-2xl bg-gray-600 p-4 flex-1">
34
+ Sesión usando técnica Convencional
35
+ </h1>
36
+ </header>
37
+
38
+ {% if error %}
39
+ <hr>
40
+ <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
41
+ <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
42
+ {{ error }}
43
+ </p>
44
+ </article>
45
+ {% endif %}
46
+
47
+ <article class="hidden">
48
+ <ul class="list-words">
49
+ {% for word in words %}
50
+ <li class="item-word">
51
+ <p class="word-id">
52
+ {{ word.id }}
53
+ </p>
54
+ <p class="word-name">
55
+ {{ word.nombre_palabra }}
56
+ </p>
57
+ </li>
58
+ {% endfor %}
59
+ </ul>
60
+
61
+ <ul class="list-tags">
62
+ {% for tag in tags %}
63
+ <li class="item-tag">
64
+ <p class="tag-id">
65
+ {{ tag.posicion }}
66
+ </p>
67
+ <p class="tag-name">
68
+ {{ tag.id_etiqueta }}
69
+ </p>
70
+ </li>
71
+ {% endfor %}
72
+ </ul>
73
+
74
+ <div class="scale-data">
75
+ <p class="scale-type">
76
+ {{ scale.id_tipo_escala }}
77
+ </p>
78
+ <p class="scale-size">
79
+ {{ scale.longitud }}
80
+ </p>
81
+ </div>
82
+ </article>
83
+
84
+ <article class="rounded flex flex-col gap-4">
85
+ <section class="flex items-center justify-center bg-gray-200 p-2 rounded-lg">
86
+ <p class="text-lg font-medium text-center">
87
+ {{ session.tecnica.instrucciones }}
88
+ </p>
89
+ </section>
90
+ <section class="flex items-center justify-center flex-wrap gap-4">
91
+ <div class="bg-gray-200 p-2 rounded-lg flex-1">
92
+ <p class="text-lg font-bold text-center">
93
+ Producto:
94
+ </p>
95
+ <p class="text-2xl font-bold text-center">
96
+ {{ product }}
97
+ </p>
98
+ </div>
99
+ <div class="bg-gray-200 p-2 rounded-lg flex-1">
100
+ <p class="text-lg font-bold text-center">
101
+ Repetición:
102
+ </p>
103
+ <p class="text-2xl font-bold text-center">
104
+ {{ session.tecnica.repecion }}
105
+ </p>
106
+ </div>
107
+ </section>
108
+ </article>
109
+
110
+ <article class="scales-container [&>*:not(:last-child)]:mb-5 flex flex-col items-center justify-center">
111
+ {% if type_scale == "continua" %}
112
+ {% for word in words %}
113
+ <form action="" method="post" class="form-rating-{{word}}">
114
+ {% csrf_token %}
115
+ <article class="bg-gray-200 p-6 rounded-lg mb-3 w-fit">
116
+ <label for="id-range-word-{{word}}"
117
+ class="text-xl font-bold tracking-wide block mb-6 first-letter:uppercase">{{ word }}</label>
118
+
119
+ <section class="block">
120
+ <div class="relative">
121
+ <input id="id-range-word-{{word}}" type="range" min="0" max="1000" value="500"
122
+ name="rating-word"
123
+ class="h-2 bg-gray-400 rounded-lg appearance-none cursor-pointer slider"
124
+ style="width: {{scale.longitud}}cm;">
125
+
126
+ <div class="absolute top-0 left-0 w-0.5 transform -translate-x-1/2 h-full bg-red-500 z-10">
127
+ </div>
128
+
129
+ <div
130
+ class="absolute top-1/5 left-1/2 w-0.5 transform -translate-x-1/2 h-4/5 bg-red-500 z-10">
131
+ </div>
132
+
133
+ <div class="absolute top-0 right-0 w-0.5 transform -translate-x-1/2 h-full bg-red-500 z-10">
134
+ </div>
135
+ </div>
136
+
137
+ <div class="flex justify-between mt-4">
138
+ <div class="flex flex-col items-start text-left w-[100px]">
139
+ <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
140
+ {{ tags.0.id_etiqueta }}
141
+ </span>
142
+ </div>
143
+ <div class="flex flex-col items-start text-center w-[100px]">
144
+ <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
145
+ {{ tags.1.id_etiqueta }}
146
+ </span>
147
+ </div>
148
+ <div class="flex flex-col items-start text-right w-[100px]">
149
+ <span class="text-xs font-medium text-gray-700 break-words first-letter:capitalize">
150
+ {{ tags.2.id_etiqueta }}
151
+ </span>
152
+ </div>
153
+ </div>
154
+ </section>
155
+ </article>
156
+ <article class="flex flex-col justify-center gap-2 items-end actions-{{word}}">
157
+ <section class="flex justify-end items-center gap-2 btns-container">
158
+ <button type="button" onclick="checkSendRating('{{word}}')"
159
+ class="ct-btn-check-{{word}} text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-blue-500 border-blue-800 transition-all rounded-xl bg-blue-500 text-white w-fit disabled:bg-amber-600">
160
+ ¿Guardar calificación?
161
+ </button>
162
+ <button type="button" onclick="sendRating('{{word}}')"
163
+ class="ct-btn-submit-{{word}} text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-500 text-white w-fit disabled:bg-amber-600 hidden">
164
+ Estoy seguro
165
+ </button>
166
+ <button type="button" onclick="cancelSendRating('{{word}}')"
167
+ class="ct-btn-cancel-{{word}} text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-orange-500 border-orange-800 transition-all rounded-xl bg-orange-500 text-white w-fit disabled:bg-amber-600 hidden">
168
+ Cancelar
169
+ </button>
170
+ </section>
171
+ </article>
172
+ </form>
173
+ {% endfor %}
174
+ {% endif %}
175
+ </article>
176
+ </article>
177
+ </article>
178
+ {% endblock %}
179
+
180
+ {% block extra_js %}
181
+ <script src="{% static 'js/created-scale.js' %}"></script>
182
+ {% endblock %}
tecnicas/templates/tecnicas/forms_tester/main_tester.html CHANGED
@@ -28,6 +28,16 @@
28
  </article>
29
  {% endif %}
30
 
 
 
 
 
 
 
 
 
 
 
31
  <hr>
32
 
33
  <article
@@ -88,6 +98,12 @@
88
  <hr>
89
 
90
  <article class="flex flex-wrap gap-10">
 
 
 
 
 
 
91
  <button
92
  class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2"
93
  onclick="startTest()">
@@ -96,6 +112,7 @@
96
  <img src="{% static 'img/check.svg' %}" alt="flechas girando" class="invert">
97
  </figure>
98
  </button>
 
99
  <button
100
  class="flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-red-500 border-red-800 transition-all rounded-xl bg-red-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2">
101
  Salir se la sesión
 
28
  </article>
29
  {% endif %}
30
 
31
+ {% if message %}
32
+ <hr>
33
+
34
+ <article class="bg-blue-600 p-4 text-white rounded-xl ct-notification-error">
35
+ <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
36
+ {{ message }}
37
+ </p>
38
+ </article>
39
+ {% endif %}
40
+
41
  <hr>
42
 
43
  <article
 
98
  <hr>
99
 
100
  <article class="flex flex-wrap gap-10">
101
+ {% if has_ended %}
102
+ <button
103
+ class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2" disabled>
104
+ Finalizaste la sesión
105
+ </button>
106
+ {% else %}
107
  <button
108
  class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2"
109
  onclick="startTest()">
 
112
  <img src="{% static 'img/check.svg' %}" alt="flechas girando" class="invert">
113
  </figure>
114
  </button>
115
+ {% endif %}
116
  <button
117
  class="flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-red-500 border-red-800 transition-all rounded-xl bg-red-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2">
118
  Salir se la sesión
tecnicas/templates/tecnicas/layouts/base.html CHANGED
@@ -18,7 +18,7 @@
18
  </head>
19
 
20
  <body>
21
- <main class="flex flex-col w-screen h-screen overflow-x-hidden overflow-y-scroll bg-gray-600">
22
  {% block content %}{% endblock %}
23
  </main>
24
 
 
18
  </head>
19
 
20
  <body>
21
+ <main class="flex flex-col w-screen max-sm:w-full h-screen overflow-x-hidden max-xs:overflow-x-scroll overflow-y-scroll bg-gray-600">
22
  {% block content %}{% endblock %}
23
  </main>
24
 
tecnicas/urls.py CHANGED
@@ -72,6 +72,10 @@ urlpatterns = [
72
  views.mainTesterForm,
73
  name="catador_main"),
74
 
 
 
 
 
75
  # APIs
76
  path("nueva-etiqueta",
77
  views.newTag,
@@ -80,4 +84,8 @@ urlpatterns = [
80
  path("api/palabras",
81
  views.words,
82
  name="api_palabras"),
 
 
 
 
83
  ]
 
72
  views.mainTesterForm,
73
  name="catador_main"),
74
 
75
+ path("testers/en-session/convencional",
76
+ views.convencionalScales,
77
+ name="session_convencional"),
78
+
79
  # APIs
80
  path("nueva-etiqueta",
81
  views.newTag,
 
84
  path("api/palabras",
85
  views.words,
86
  name="api_palabras"),
87
+
88
+ path("testers/api/ratingword",
89
+ views.reatingWord,
90
+ name="api_rating_word"),
91
  ]
tecnicas/views/__init__.py CHANGED
@@ -18,5 +18,7 @@ from .tester_management.tester_search import testerSearch
18
 
19
  from .apis.api_tag import newTag
20
  from .apis.api_words import words
 
21
 
22
  from .tester_forms.main_tester_form import mainTesterForm
 
 
18
 
19
  from .apis.api_tag import newTag
20
  from .apis.api_words import words
21
+ from .apis.rating_word import reatingWord
22
 
23
  from .tester_forms.main_tester_form import mainTesterForm
24
+ from .tester_forms.convencional_scales import convencionalScales
tecnicas/views/apis/rating_word.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest, JsonResponse
2
+
3
+
4
+ def reatingWord(req: HttpRequest):
5
+ if req.method == "POST":
6
+ if not req.POST["rating-word"]:
7
+ return JsonResponse({"error": "No se ha reconocido valor para la calificacion"})
8
+ rating = req.POST["rating-word"]
9
+ word = req.POST["name-word"]
10
+ return JsonResponse({
11
+ "message": "Ok",
12
+ "data": {
13
+ "word": word,
14
+ "rating": rating,
15
+ "cata_ser": req.session["cata_username"]
16
+ }
17
+ })
tecnicas/views/login_tester.py CHANGED
@@ -21,6 +21,8 @@ def testerLogin(req: HttpRequest):
21
  if isinstance(existCredentials, dict):
22
  context = {"error": existCredentials["error"]}
23
  return render(req, "tecnicas/cata-login.html", context)
 
 
24
 
25
  taster_participation = login_controller.validateEntry()
26
  if isinstance(taster_participation, dict):
@@ -29,6 +31,8 @@ def testerLogin(req: HttpRequest):
29
 
30
  req.session["cata_username"] = tester_user
31
  req.session["code_session"] = session_code
 
 
32
 
33
  req.session.set_expiry(20*60)
34
  return redirect(reverse("cata_system:catador_main"))
 
21
  if isinstance(existCredentials, dict):
22
  context = {"error": existCredentials["error"]}
23
  return render(req, "tecnicas/cata-login.html", context)
24
+
25
+ session = existCredentials[1]
26
 
27
  taster_participation = login_controller.validateEntry()
28
  if isinstance(taster_participation, dict):
 
31
 
32
  req.session["cata_username"] = tester_user
33
  req.session["code_session"] = session_code
34
+ req.session["id_techniqe"] = session.tecnica.id
35
+ req.session["id_participation"] = taster_participation.id
36
 
37
  req.session.set_expiry(20*60)
38
  return redirect(reverse("cata_system:catador_main"))
tecnicas/views/tester_forms/convencional_scales.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from ...controllers import SesionController, PosicionController, CalificacionController, ParticipacionController, PalabrasController, EscalaController, DatoController
5
+
6
+ '''
7
+ **** Esta vista para sesion con tecnica convencional de escalas, al entrar debe:
8
+ **** ****
9
+
10
+ ++++ Por el lado del servidor
11
+ ++++ ++++
12
+ * Obtner los productos que se evaluan en la tecnica
13
+ * Ordenar los productos segun la Poscion en que se encuentre en el Orden ya establecidos
14
+ * Obtner las palabras para evaluar
15
+ - Revisar que estilo usan
16
+ - Obtner las palabras si el estilo es atributos
17
+ - Obtner las palabras si el estilo es Vocabulario
18
+ - El Catador plasma sus resultados para las palabras sin importar el estilo
19
+ * Comprobar que productos se han calificado
20
+ - Revisar el numero de palabras
21
+ - Comenzar con el primer producto e ir revisando uno a uno
22
+ - Revisar el numero de calificaciones del producto
23
+ - Numero de calificaciones del producto == 0
24
+ - Continuar con la evaluacion o comezar con este producto
25
+ - Numero de calificaciones del producto < numero de palabras
26
+ - Continuar con la evaluacion
27
+ - Numero de calificacion == numero de palabras
28
+ - Pasar con el siguiente producto
29
+ * Si no quedan mas productos por revisar
30
+ - Participacion.finalizado = True
31
+ - Participacion.activo = False
32
+ - Redirigir a "catador_main"
33
+ * Optner la siguiente palabra sin calificar
34
+ - Obtener las calificaiones de producto
35
+ - Obtener los datos de las calificaciones
36
+ - Comprobar que palabras no estan tienen dato
37
+ - Mandar palabras para el usuario
38
+ * Obtener informacion de la escala para mandar
39
+
40
+ ++++ Por el lado del cliente
41
+ ++++ ++++
42
+ + Mostrar en todo momento las instrucciones en la parte superior de la pagina
43
+ + Mostrar la repeticion en la que esta
44
+ + Mostrar el producto que esta calificando
45
+ + Desglozar las palabras para calificar
46
+ - Cada palabra debe contar con su input segun el tipo
47
+ - Para cada input se debe poder guardar la calificacion
48
+ - Anstes de guardar la calificacion preguntar por la confirmacion a la hora de guardar el dato
49
+ - Especficar las etiquetas por debajo del input de ripo rango
50
+ - Para las escalas de tipo continua
51
+ - La longitud de la barra de la escala debe ser igual al tamaño que se especifico en la configuracion
52
+ - Contar con un input de tipo range
53
+ - Contar con etiqueta en el inicio de la barra, en el medio y al final
54
+ - La escala debe terner marcas al inicio, medio y final
55
+ - El rango de la barra debe ir de 0 a 1000
56
+ - Para las escalas de tipo estructurada
57
+ - Su longitud sera tan largo como el contendor que lo aloja
58
+ - La barra se divide segun el numero de etiquetas que estas posean
59
+ - Cata longitud debe poser una marca y solo estas seran las unicas posibles respuestas
60
+ - Cata segmento en el que se divide debe tener la etiqueda correspondiente por debajo
61
+ '''
62
+
63
+
64
+ def convencionalScales(req: HttpRequest):
65
+ if not "id_order" in req.session:
66
+ return redirect(reverse("cata_system:catador_main"))
67
+
68
+ session = SesionController.getSessionByCode(req.session["code_session"])
69
+ technique = session.tecnica
70
+
71
+ context = {
72
+ "session": session
73
+ }
74
+
75
+ if req.method == "GET":
76
+ positions = PosicionController.getPostionsInOrder(
77
+ id_order=req.session["id_order"])
78
+
79
+ sorted_positions = sorted(positions, key=lambda posi: posi.posicion)
80
+
81
+ words = PalabrasController.getWordsInTechnique(technique=technique)
82
+
83
+ next_position = CalificacionController.checkProducsWithoutRating(
84
+ positions=sorted_positions,
85
+ user_cata=req.session["cata_username"],
86
+ id_technique=req.session["id_techniqe"],
87
+ repetition=session.tecnica.repecion,
88
+ technique=technique,
89
+ num_words=len(words)
90
+ )
91
+
92
+ if isinstance(next_position, dict):
93
+ updated_participation = ParticipacionController.finishSession(
94
+ req.session["id_participation"])
95
+ return redirect(reverse("cata_system:catador_main"))
96
+
97
+ if isinstance(next_position, list):
98
+ next_position = next_position[0]
99
+
100
+ context["product"] = next_position.id_producto
101
+
102
+ ratings_product = CalificacionController.getRatings(
103
+ technique=technique,
104
+ product=next_position.id_producto,
105
+ repetition=technique.repecion,
106
+ user_tester=req.session["cata_username"]
107
+ )
108
+
109
+ if isinstance(ratings_product, dict):
110
+ context["error"] = ratings_product["error"]
111
+ return render(req, "tecnicas/forms_tester/convencional.html", context)
112
+ elif not ratings_product:
113
+ context["words"] = words
114
+ else:
115
+ recoreded_data = DatoController.getRerecordedData(ratings=ratings_product)
116
+ if not recoreded_data:
117
+ context["words"] = words
118
+
119
+ words_to_use = PalabrasController.getWordsWithoutData(recoreded_data=recoreded_data, words=words)
120
+ context["words"] = words_to_use
121
+
122
+ scale = EscalaController.getScaleByTechnique(technique=technique)
123
+ context["scale"] = scale
124
+ context["type_scale"] = scale.id_tipo_escala.nombre_escala
125
+
126
+ use_tags = EscalaController.getRelatedTagsInScale(scale=scale)
127
+ context["tags"] = use_tags
128
+
129
+ return render(req, "tecnicas/forms_tester/convencional.html", context)
tecnicas/views/tester_forms/main_tester_form.py CHANGED
@@ -1,7 +1,7 @@
1
  from django.http import HttpRequest, JsonResponse
2
- from django.shortcuts import render
3
- from ...controllers import SesionController, MainTesterFormController
4
- from ...models import Orden
5
 
6
 
7
  def mainTesterForm(req: HttpRequest):
@@ -12,28 +12,45 @@ def mainTesterForm(req: HttpRequest):
12
  "session": session
13
  }
14
 
 
 
 
15
  if req.method == "GET":
 
 
 
 
 
 
 
 
16
  return render(req, "tecnicas/forms_tester/main_tester.html", context)
17
  elif req.method == "POST":
18
  if req.POST["action"] == "start_posting":
19
- view_controller = MainTesterFormController(
20
- req.session["code_session"], req.session["cata_username"])
 
 
 
 
21
 
22
- order = view_controller.checkAssignOrder()
23
- if not isinstance(order, dict):
24
- req.session["id_order"] = order.id
25
- context["error"] = "Catador tiene orden"
26
- return render(req, "tecnicas/forms_tester/main_tester.html", context)
27
 
28
  order = view_controller.assignOrder()
29
  if isinstance(order, dict):
30
  context["error"] = order["error"]
31
  return render(req, "tecnicas/forms_tester/main_tester.html", context)
32
 
33
- print(order)
34
- return render(req, "tecnicas/forms_tester/main_tester.html", context)
 
 
 
 
 
35
  elif req.POST["action"] == "close_session":
36
- pass
 
37
  else:
38
  context["error"] = "Acción sin especificar"
39
  return render(req, "tecnicas/forms_tester/main_tester.html", context)
 
1
  from django.http import HttpRequest, JsonResponse
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from ...controllers import SesionController, MainTesterFormController, ParticipacionController
5
 
6
 
7
  def mainTesterForm(req: HttpRequest):
 
12
  "session": session
13
  }
14
 
15
+ view_controller = MainTesterFormController(
16
+ req.session["code_session"], req.session["cata_username"])
17
+
18
  if req.method == "GET":
19
+ order = view_controller.checkAssignOrder()
20
+
21
+ if not isinstance(order, dict):
22
+ req.session["id_order"] = order.id
23
+ if view_controller.isEndedSession(id_participation=req.session["id_participation"], repetition=session.tecnica.repecion):
24
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
25
+ context["has_ended"] = True
26
+
27
  return render(req, "tecnicas/forms_tester/main_tester.html", context)
28
  elif req.method == "POST":
29
  if req.POST["action"] == "start_posting":
30
+ if "id_order" in req.session:
31
+ update_participation = ParticipacionController.enterSession(
32
+ id_participation=req.session["id_participation"])
33
+ if isinstance(update_participation, dict):
34
+ context["error"] = update_participation["error"]
35
+ return render(req, "tecnicas/forms_tester/main_tester.html", context)
36
 
37
+ return redirect(reverse("cata_system:session_convencional"))
 
 
 
 
38
 
39
  order = view_controller.assignOrder()
40
  if isinstance(order, dict):
41
  context["error"] = order["error"]
42
  return render(req, "tecnicas/forms_tester/main_tester.html", context)
43
 
44
+ update_participation = ParticipacionController.enterSession(
45
+ id_participation=req.session["id_participation"])
46
+ if isinstance(update_participation, dict):
47
+ context["error"] = update_participation["error"]
48
+ return render(req, "tecnicas/forms_tester/main_tester.html", context)
49
+
50
+ return redirect(reverse("cata_system:session_convencional"))
51
  elif req.POST["action"] == "close_session":
52
+ req.session.flush()
53
+ return redirect(reverse("cata_system:catador_login"))
54
  else:
55
  context["error"] = "Acción sin especificar"
56
  return render(req, "tecnicas/forms_tester/main_tester.html", context)