chartManD commited on
Commit
fd0f431
·
2 Parent(s): 0e5431a 31ea9c3

Merge branch 'dev'

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. tecnicas/admin.py +9 -15
  2. tecnicas/controllers/__init__.py +41 -10
  3. tecnicas/controllers/api_controller/rating_cata_controller.py +78 -0
  4. tecnicas/controllers/api_controller/rating_napping_controller.py +367 -0
  5. tecnicas/controllers/api_controller/rating_pf_list_controller.py +221 -0
  6. tecnicas/controllers/{views_controller/api_rating_controller.py → api_controller/rating_sacales_controller.py} +2 -2
  7. tecnicas/controllers/api_controller/rating_sort_controller.py +67 -0
  8. tecnicas/controllers/models_controller/calificacion_controller.py +1 -1
  9. tecnicas/controllers/models_controller/dato_controller.py +36 -14
  10. tecnicas/controllers/models_controller/escala_controller.py +19 -13
  11. tecnicas/controllers/models_controller/particiapacion_controller.py +3 -13
  12. tecnicas/controllers/models_controller/sesion_controller.py +3 -8
  13. tecnicas/controllers/models_controller/tecnica_controller.py +1 -1
  14. tecnicas/controllers/views_controller/create_session/panel_basic_controller.py +106 -13
  15. tecnicas/controllers/views_controller/create_session/panel_codes_controller.py +38 -9
  16. tecnicas/controllers/views_controller/create_session/panel_tags_controller.py +2 -2
  17. tecnicas/controllers/views_controller/create_session/panel_words_controller.py +1 -1
  18. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_cata_controller.py +137 -0
  19. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_controller.py +17 -0
  20. tecnicas/controllers/views_controller/create_session/{panel_create_controller.py → panels_create/panel_create_escalas_controller.py} +7 -284
  21. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_napping_controller.py +114 -0
  22. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_pf_controller.py +105 -0
  23. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_rata_controller.py +155 -0
  24. tecnicas/controllers/views_controller/create_session/panels_create/panel_create_sort_controller.py +95 -0
  25. tecnicas/controllers/views_controller/session_management/{details_rata_controller.py → details/details_cata_controller.py} +36 -20
  26. tecnicas/controllers/views_controller/session_management/details/details_controller.py +70 -0
  27. tecnicas/controllers/views_controller/session_management/{details_escala_controller.py → details/details_escala_controller.py} +9 -55
  28. tecnicas/controllers/views_controller/session_management/details/details_napping_controller.py +508 -0
  29. tecnicas/controllers/views_controller/session_management/details/details_pf_controller.py +215 -0
  30. tecnicas/controllers/views_controller/session_management/details/details_rata_controller.py +7 -0
  31. tecnicas/controllers/views_controller/session_management/details/details_sort_controller.py +91 -0
  32. tecnicas/controllers/views_controller/session_management/details_controller.py +0 -13
  33. tecnicas/controllers/views_controller/session_management/{monitor_controller.py → monitor/monitor_controller.py} +21 -12
  34. tecnicas/controllers/views_controller/session_management/{monitor_escalas_controller.py → monitor/monitor_escalas_controller.py} +1 -17
  35. tecnicas/controllers/views_controller/session_management/monitor/monitor_napping_controller.py +35 -0
  36. tecnicas/controllers/views_controller/session_management/monitor/monitor_pf_controller.py +72 -0
  37. tecnicas/controllers/views_controller/session_management/{monitor_rata_controller.py → monitor/monitor_rata_controller.py} +1 -17
  38. tecnicas/controllers/views_controller/session_management/monitor/monitor_sort_controller.py +27 -0
  39. tecnicas/controllers/views_controller/sessions_tester/{init_session_tester_controller.py → init_session/init_session_controller.py} +6 -79
  40. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_escalas_controller.py +82 -0
  41. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_napping_controller.py +80 -0
  42. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_pf_controller.py +144 -0
  43. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_rata_controller.py +24 -0
  44. tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_sort_controller.py +77 -0
  45. tecnicas/controllers/views_controller/sessions_tester/login_session_tester_controller.py +82 -18
  46. tecnicas/controllers/views_controller/sessions_tester/tests_forms/general_test_controller.py +28 -0
  47. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_cata_controller.py +55 -0
  48. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_napping_controller.py +242 -0
  49. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_pf_controller.py +174 -0
  50. tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_rata_controller.py +87 -0
tecnicas/admin.py CHANGED
@@ -1,20 +1,6 @@
1
  from django.contrib import admin
2
 
3
- from .models import CategoriaTecnica, TipoTecnica, TipoEscala, EstiloPalabra
4
-
5
- from .models import Catador, Presentador
6
-
7
- from .models import Tecnica, SesionSensorial
8
-
9
- from .models import EsAtributo, Palabra, Vocabulario
10
-
11
- from .models import Etiqueta, Escala, EtiquetasEscala
12
-
13
- from .models import Producto, Participacion
14
-
15
- from .models import Orden, Posicion
16
-
17
- from .models import Dato, ValorDecimal, ValorBooleano, Calificacion
18
 
19
  # Register your models here.
20
  admin.site.register(CategoriaTecnica)
@@ -46,3 +32,11 @@ admin.site.register(Dato)
46
  admin.site.register(ValorDecimal)
47
  admin.site.register(ValorBooleano)
48
  admin.site.register(Calificacion)
 
 
 
 
 
 
 
 
 
1
  from django.contrib import admin
2
 
3
+ from .models import CategoriaTecnica, TipoTecnica, TipoEscala, EstiloPalabra, Catador, Presentador, Tecnica, SesionSensorial, EsAtributo, Palabra, Vocabulario, Etiqueta, Escala, EtiquetasEscala, Producto, Participacion, Orden, Posicion, Dato, ValorDecimal, ValorBooleano, Calificacion, ListaPalabras, GrupoProducto, Modalidad, TecnicaModalidad, DatoPunto
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  # Register your models here.
6
  admin.site.register(CategoriaTecnica)
 
32
  admin.site.register(ValorDecimal)
33
  admin.site.register(ValorBooleano)
34
  admin.site.register(Calificacion)
35
+
36
+ admin.site.register(ListaPalabras)
37
+ admin.site.register(GrupoProducto)
38
+
39
+ admin.site.register(Modalidad)
40
+ admin.site.register(TecnicaModalidad)
41
+
42
+ admin.site.register(DatoPunto)
tecnicas/controllers/__init__.py CHANGED
@@ -16,22 +16,53 @@ from .views_controller.create_session.panel_basic_controller import PanelBasicCo
16
  from .views_controller.create_session.panel_tags_controller import PanelTagsController
17
  from .views_controller.create_session.panel_codes_controller import PanelCodesController
18
  from .views_controller.create_session.panel_words_controller import PanelWordsController
19
- from .views_controller.create_session.panel_create_controller import PanelCreateController
20
 
21
- from .views_controller.session_management.details_controller import DetallesController
22
- from .views_controller.session_management.details_escala_controller import DetallesEscalasController
23
- from .views_controller.session_management.details_rata_controller import DetallesRATAController
24
- from .views_controller.session_management.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
 
 
 
 
16
  from .views_controller.create_session.panel_tags_controller import PanelTagsController
17
  from .views_controller.create_session.panel_codes_controller import PanelCodesController
18
  from .views_controller.create_session.panel_words_controller import PanelWordsController
 
19
 
20
+ from .views_controller.create_session.panels_create.panel_create_escalas_controller import PanelCreateEscalasController
21
+ from .views_controller.create_session.panels_create.panel_create_rata_controller import PanelCreateRataController
22
+ from .views_controller.create_session.panels_create.panel_create_cata_controller import PanelCreateCataController
23
+ from .views_controller.create_session.panels_create.panel_create_pf_controller import PanelCreatePFController
24
+ from .views_controller.create_session.panels_create.panel_create_sort_controller import PanelCreateSortController
25
+ from .views_controller.create_session.panels_create.panel_create_napping_controller import PanelCreateNappingController
26
+
27
+
28
+ from .views_controller.session_management.details.details_controller import DetallesController
29
+ from .views_controller.session_management.details.details_escala_controller import DetallesEscalasController
30
+ from .views_controller.session_management.details.details_rata_controller import DetallesRATAController
31
+ from .views_controller.session_management.details.details_cata_controller import DetallesCATAController
32
+ from .views_controller.session_management.details.details_pf_controller import DetallesPFController
33
+ from .views_controller.session_management.details.details_sort_controller import DetallesSortController
34
+ from .views_controller.session_management.details.details_napping_controller import DetallesNappingController
35
+
36
+ from .views_controller.session_management.monitor.monitor_escalas_controller import MonitorEscalasController
37
+ from .views_controller.session_management.monitor.monitor_rata_controller import MonitorRATAController
38
+ from .views_controller.session_management.monitor.monitor_pf_controller import MonitorPFController
39
+ from .views_controller.session_management.monitor.monitor_sort_controller import MonitorSortController
40
+ from .views_controller.session_management.monitor.monitor_napping_controller import MonitorNappingController
41
 
42
  from .views_controller.sessions_tester.login_session_tester_controller import LoginSessionTesterController
 
 
43
  from .views_controller.sessions_tester.list_sessions_tester_controller import ListSessionsTesterController
44
 
45
+ from .views_controller.sessions_tester.tests_forms.test_scales_controller import TestScalesController
46
+ from .views_controller.sessions_tester.tests_forms.test_rata_controller import TestRataController
47
+ from .views_controller.sessions_tester.tests_forms.test_cata_controller import TestCataController
48
+ from .views_controller.sessions_tester.tests_forms.test_pf_controller import TestPFController
49
+ from .views_controller.sessions_tester.tests_forms.test_sort_controller import TestSortController
50
+ from .views_controller.sessions_tester.tests_forms.test_napping_controller import TestNappingController
51
+
52
+ from .views_controller.sessions_tester.init_session.init_session_escalas_controller import InitSessionEscalasController
53
+ from .views_controller.sessions_tester.init_session.init_session_rata_controller import InitSessionRATAController
54
+ from .views_controller.sessions_tester.init_session.init_session_pf_controller import InitSessionPFController
55
+ from .views_controller.sessions_tester.init_session.init_session_sort_controller import InitSessionSortController
56
+ from .views_controller.sessions_tester.init_session.init_session_napping_controller import InitSessionNappingController
57
+
58
  from .views_controller.vocabulary_manage.create_vocabulary_controller import CreateVocabularyController
59
+ from .views_controller.vocabulary_manage.view_vocabulary_controller import ViewVocabularyController
60
  from .views_controller.vocabulary_manage.list_vocabulary_controller import ListVocabularyController
61
 
62
+ from .api_controller.rating_sacales_controller import RatingScalesController
63
+ from .api_controller.rating_cata_controller import RatingCataController
64
+ from .api_controller.rating_pf_list_controller import RatingPFListController
65
  from .views_controller.tester_list_controller import TesterListController
66
+ from .api_controller.rating_sort_controller import RatingSortController
67
+ from .api_controller.rating_napping_controller import RatingNappingController
68
+
tecnicas/controllers/api_controller/rating_cata_controller.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse, HttpRequest
2
+ from django.db import transaction
3
+ from tecnicas.models import Calificacion, Dato, ValorBooleano, Participacion, Palabra
4
+
5
+
6
+ class RatingCataController():
7
+ def __init__(self):
8
+ pass
9
+
10
+ @staticmethod
11
+ def saveRatingWords(request: HttpRequest, data_words: list[dict], data_prodct: dict):
12
+ try:
13
+ with transaction.atomic():
14
+ participation = Participacion.objects.get(
15
+ id=request.session["id_participation"])
16
+ if not participation:
17
+ raise ValueError("No está autorizado en la sesión")
18
+
19
+ ids_words = []
20
+ words_values = {}
21
+
22
+ # Acoplar datos para usar
23
+ for da_wo in data_words:
24
+ ids_words.append(da_wo["id"])
25
+ words_values[da_wo["word"]] = da_wo["is_check"]
26
+
27
+ words_for_rating = Palabra.objects.filter(id__in=ids_words)
28
+ if not words_for_rating:
29
+ raise ValueError("No se han encontrado sus palabras")
30
+
31
+ technique = participation.tecnica
32
+
33
+ # Creando la calificacion
34
+ (rating, creataed) = Calificacion.objects.get_or_create(
35
+ num_repeticion=technique.repeticion,
36
+ id_producto_id=data_prodct["id"],
37
+ id_tecnica=technique,
38
+ id_catador=request.user.user_catador
39
+ )
40
+ if not rating:
41
+ raise ValueError("Problemas al crear la calificación")
42
+
43
+ # Guardando datos
44
+ data_for_save = []
45
+ for word in words_for_rating:
46
+ data_for_save.append(Dato(
47
+ id_palabra=word,
48
+ id_calificacion=rating
49
+ ))
50
+
51
+ Dato.objects.bulk_create(data_for_save)
52
+ data_saved = Dato.objects.filter(
53
+ id_calificacion=rating).only("id_palabra")
54
+ if not data_saved:
55
+ raise ValueError("Problemas al crear los datos")
56
+
57
+ # Guardando valores de datos
58
+ values_for_save = []
59
+ for data in data_saved:
60
+ word_for_rating = data.id_palabra.nombre_palabra
61
+ values_for_save.append(
62
+ ValorBooleano(
63
+ id_dato=data,
64
+ valor=words_values[word_for_rating]
65
+ )
66
+ )
67
+
68
+ ValorBooleano.objects.bulk_create(values_for_save)
69
+ values_saves = ValorBooleano.objects.filter(
70
+ id_dato__id_calificacion=rating).count()
71
+ if not values_saves:
72
+ raise ValueError("Error al guardar los datos")
73
+
74
+ return JsonResponse({"message": "Valores guardados"})
75
+
76
+ except ValueError as e:
77
+ print(f"Error de calificacion: {e}")
78
+ return JsonResponse({"error": e}, statusstatus=500)
tecnicas/controllers/api_controller/rating_napping_controller.py ADDED
@@ -0,0 +1,367 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse
2
+ from django.http import HttpRequest
3
+ from django.db import transaction
4
+ from tecnicas.models import Calificacion, DatoPunto, Producto, Participacion, Palabra, GrupoProducto, TecnicaModalidad
5
+ from tecnicas.forms import ListWordsForm
6
+
7
+
8
+ class RatingNappingController:
9
+ @staticmethod
10
+ def saveRatingCoordinates(request: HttpRequest, data: list | dict):
11
+ participation = Participacion.objects.get(
12
+ id=request.session["id_participation"]
13
+ )
14
+
15
+ name_mod = TecnicaModalidad.objects.get(
16
+ tecnica=participation.tecnica
17
+ ).modalidad.nombre.lower()
18
+
19
+ # Branch based on modality
20
+ if name_mod == 'sorting':
21
+ return RatingNappingController.processSortingMode(
22
+ data, participation
23
+ )
24
+
25
+ if name_mod in ['sin modalidad', 'perfil ultra flash']:
26
+ return RatingNappingController.processNappOrPUF(
27
+ data, participation
28
+ )
29
+
30
+ else:
31
+ return JsonResponse({"error": "Modalidad no soportada"})
32
+
33
+ @staticmethod
34
+ def processSortingMode(data: dict, participation):
35
+ try:
36
+ with transaction.atomic():
37
+ # Extract products array (always present)
38
+ products = data.get("products", [])
39
+ if not products:
40
+ return JsonResponse({"error": "No se proporcionaron productos"})
41
+
42
+ existing_ratings_map, products_map = RatingNappingController.savePoints(
43
+ isSorting=True, products=products, participation=participation
44
+ )
45
+
46
+ # Process groups if they exist
47
+ groups = data.get("groups", {})
48
+ if groups:
49
+ RatingNappingController.processGroupsForSorting(
50
+ products, groups, participation, existing_ratings_map, products_map
51
+ )
52
+ else:
53
+ RatingNappingController.deleteAllGroups(participation)
54
+
55
+ return JsonResponse({"message": "Datos guardados exitosamente"})
56
+
57
+ except Exception as e:
58
+ print("ERROR:", e)
59
+ import traceback
60
+ traceback.print_exc()
61
+ return JsonResponse({"error": f"Error al procesar datos: {str(e)}"})
62
+
63
+ @staticmethod
64
+ def processNappOrPUF(data: list, participation):
65
+ try:
66
+ with transaction.atomic():
67
+ existing_ratings_map, products_map = RatingNappingController.savePoints(
68
+ isSorting=False, products=data, participation=participation
69
+ )
70
+
71
+ RatingNappingController.processWordsForRatings(
72
+ data, existing_ratings_map
73
+ )
74
+
75
+ return JsonResponse({"message": "Datos guardados exitosamente"})
76
+
77
+ except Exception as e:
78
+ print("ERROR:", e)
79
+ return JsonResponse({"error": "Error al procesar datos"})
80
+
81
+ @staticmethod
82
+ def processGroupsForSorting(products, groups, participation, existing_ratings_map, products_map):
83
+ """Process groups for sorting mode
84
+ - Creates/updates GrupoProducto instances
85
+ - Ensures products don't belong to multiple groups
86
+ - Associates words with groups
87
+ """
88
+ # Build mapping of product_id to group_id from products array
89
+ product_to_group = {}
90
+ for product_item in products:
91
+ group_code = product_item.get("group", "")
92
+ if group_code: # Only if product has a group assigned
93
+ product_id = int(product_item["idProduct"])
94
+ if product_id in product_to_group:
95
+ raise ValueError(
96
+ f"Producto {product_id} pertenece a múltiples grupos")
97
+ product_to_group[product_id] = group_code
98
+
99
+ # Get existing groups for this catador and technique
100
+ existing_groups = GrupoProducto.objects.filter(
101
+ tecnica=participation.tecnica,
102
+ catador=participation.catador
103
+ )
104
+
105
+ # Create a map of existing groups by their product composition
106
+ # We'll identify groups by the set of products they contain
107
+ existing_groups_map = {}
108
+ for group in existing_groups:
109
+ product_ids = set(group.productos.values_list('id', flat=True))
110
+ key = frozenset(product_ids)
111
+ existing_groups_map[key] = group
112
+
113
+ # Build new groups structure
114
+ groups_to_create = []
115
+ groups_to_update = []
116
+ group_products_map = {} # group_id -> [product_ids]
117
+
118
+ # Organize products by group
119
+ for product_id, group_code in product_to_group.items():
120
+ if group_code not in group_products_map:
121
+ group_products_map[group_code] = []
122
+ group_products_map[group_code].append(product_id)
123
+
124
+ # Process each group
125
+ for group_code, product_ids in group_products_map.items():
126
+ product_set = frozenset(product_ids)
127
+
128
+ # Check if this group already exists
129
+ if product_set in existing_groups_map:
130
+ group = existing_groups_map[product_set]
131
+ groups_to_update.append((group, group_code))
132
+ else:
133
+ # Create new group
134
+ group = GrupoProducto(
135
+ tecnica=participation.tecnica,
136
+ catador=participation.catador
137
+ )
138
+ groups_to_create.append((group, product_ids, group_code))
139
+
140
+ # Create new groups
141
+ created_groups = []
142
+ for group, product_ids, group_code in groups_to_create:
143
+ group.save() # Save first to get ID for M2M
144
+
145
+ # Add products to group
146
+ productos = [products_map[pid]
147
+ for pid in product_ids if pid in products_map]
148
+ group.productos.set(productos)
149
+
150
+ created_groups.append((group, group_code))
151
+
152
+ # Combine created and existing groups for word processing
153
+ all_groups_for_words = created_groups + groups_to_update
154
+
155
+ # Delete groups that no longer exist
156
+ current_group_sets = set(frozenset(pids)
157
+ for pids in group_products_map.values())
158
+ for product_set, group in existing_groups_map.items():
159
+ if product_set not in current_group_sets:
160
+ group.delete()
161
+
162
+ # Process words for groups
163
+ if groups:
164
+ RatingNappingController.processWordsForGroups(
165
+ groups, all_groups_for_words
166
+ )
167
+
168
+ @staticmethod
169
+ def deleteAllGroups(participation):
170
+ GrupoProducto.objects.filter(
171
+ tecnica=participation.tecnica,
172
+ catador=participation.catador
173
+ ).delete()
174
+
175
+ @staticmethod
176
+ def processWordsForGroups(groups_data, groups_list):
177
+ """Process and associate words to groups
178
+ - Creates words that don't exist
179
+ - Associates words to GrupoProducto instances
180
+ - Handles concurrency
181
+ """
182
+ # Collect all unique words from all groups
183
+ all_words = set()
184
+ for group_id, words in groups_data.items():
185
+ if words:
186
+ all_words.update(words)
187
+
188
+ if not all_words:
189
+ # No words to process, just clear existing words from groups
190
+ for grupo, _ in groups_list:
191
+ grupo.palabras.clear()
192
+ return
193
+
194
+ # Get existing words
195
+ existing_words = Palabra.objects.filter(
196
+ nombre_palabra__in=all_words
197
+ )
198
+ existing_words_map = {w.nombre_palabra: w for w in existing_words}
199
+
200
+ # Create missing words with concurrency handling
201
+ word_objects = {}
202
+ for word_name in all_words:
203
+ if word_name in existing_words_map:
204
+ word_objects[word_name] = existing_words_map[word_name]
205
+ else:
206
+ word_obj, created = Palabra.objects.get_or_create(
207
+ nombre_palabra=word_name
208
+ )
209
+ word_objects[word_name] = word_obj
210
+
211
+ # Associate words with groups
212
+ for grupo, group_id in groups_list:
213
+ words = groups_data.get(group_id, [])
214
+ if words:
215
+ words_to_set = [word_objects[word_name]
216
+ for word_name in words if word_name in word_objects]
217
+ grupo.palabras.set(words_to_set)
218
+ else:
219
+ grupo.palabras.clear()
220
+
221
+ @staticmethod
222
+ def savePoints(isSorting: bool, products, participation):
223
+ try:
224
+ with transaction.atomic():
225
+ # Get products map for validation
226
+ products_map = RatingNappingController.getProductsMap(
227
+ participation.tecnica)
228
+
229
+ # Get existing ratings map
230
+ existing_ratings_map = RatingNappingController.getExistingRatingsMap(
231
+ participation.tecnica, participation.catador)
232
+
233
+ if not isSorting:
234
+ validation_result = RatingNappingController.validateWords(
235
+ products)
236
+ if validation_result is not None:
237
+ return validation_result
238
+
239
+ # Create new ratings for products that don't have them
240
+ new_ratings = []
241
+ ids_products = products_map.keys()
242
+ for item in products:
243
+ product_id = int(item["idProduct"])
244
+ if product_id not in existing_ratings_map and product_id in ids_products:
245
+ new_ratings.append(
246
+ Calificacion(
247
+ num_repeticion=0,
248
+ id_producto=products_map[product_id],
249
+ id_tecnica=participation.tecnica,
250
+ id_catador=participation.catador,
251
+ )
252
+ )
253
+
254
+ if new_ratings:
255
+ Calificacion.objects.bulk_create(new_ratings)
256
+ existing_ratings_map = RatingNappingController.getExistingRatingsMap(
257
+ participation.tecnica, participation.catador)
258
+
259
+ # Process DatoPunto instances from products array
260
+ existing_points_map = RatingNappingController.getExistingPointsMap(
261
+ existing_ratings_map.values())
262
+
263
+ points_to_create = []
264
+ points_to_update = []
265
+
266
+ for item in products:
267
+ product_id = int(item["idProduct"])
268
+ rating = existing_ratings_map.get(product_id)
269
+
270
+ if rating:
271
+ if rating.id in existing_points_map:
272
+ point = existing_points_map[rating.id]
273
+ point.x = item["x"]
274
+ point.y = item["y"]
275
+ points_to_update.append(point)
276
+ else:
277
+ points_to_create.append(
278
+ DatoPunto(
279
+ x=item["x"],
280
+ y=item["y"],
281
+ calificacion=rating,
282
+ )
283
+ )
284
+
285
+ if points_to_create:
286
+ DatoPunto.objects.bulk_create(points_to_create)
287
+
288
+ if points_to_update:
289
+ DatoPunto.objects.bulk_update(points_to_update, ['x', 'y'])
290
+
291
+ return (existing_ratings_map, products_map)
292
+
293
+ except Exception as e:
294
+ print(e)
295
+ return JsonResponse({"error": "Error al guardar los puntos"})
296
+
297
+ @staticmethod
298
+ def validateWords(data: list):
299
+ for item in data:
300
+ words = item.get("words", [])
301
+ if words:
302
+ dic_words = {}
303
+ for index, word in enumerate(words, start=1):
304
+ dic_words[f"palabra_{index}"] = word
305
+
306
+ form = ListWordsForm(dic_words, new_words=words)
307
+ if not form.is_valid():
308
+ errors = []
309
+ for field, error_list in form.errors.items():
310
+ errors.extend(error_list)
311
+ return JsonResponse({"error": f"Error en validación de palabras: {', '.join(errors)}"})
312
+ return None
313
+
314
+ @staticmethod
315
+ def processWordsForRatings(data: list, existing_ratings_map: dict):
316
+ all_words = set()
317
+ for item in data:
318
+ words = item.get("words", [])
319
+ if words:
320
+ all_words.update(words)
321
+
322
+ if not all_words:
323
+ return
324
+
325
+ existing_words = Palabra.objects.filter(
326
+ nombre_palabra__in=all_words
327
+ )
328
+ existing_words_map = {w.nombre_palabra: w for w in existing_words}
329
+
330
+ word_objects = {}
331
+ for word_name in all_words:
332
+ if word_name in existing_words_map:
333
+ word_objects[word_name] = existing_words_map[word_name]
334
+ else:
335
+ word_obj, created = Palabra.objects.get_or_create(
336
+ nombre_palabra=word_name
337
+ )
338
+ word_objects[word_name] = word_obj
339
+
340
+ for item in data:
341
+ words = item.get("words", [])
342
+ if words:
343
+ product_id = int(item["idProduct"])
344
+ rating = existing_ratings_map.get(product_id)
345
+
346
+ if rating:
347
+ words_to_set = [word_objects[word_name]
348
+ for word_name in words]
349
+ rating.palabras.set(words_to_set)
350
+
351
+ @staticmethod
352
+ def getProductsMap(id_tecnica):
353
+ products_qs = Producto.objects.filter(id_tecnica=id_tecnica)
354
+ return {p.id: p for p in products_qs}
355
+
356
+ @staticmethod
357
+ def getExistingRatingsMap(id_tecnica, id_catador):
358
+ ratings = Calificacion.objects.filter(
359
+ id_tecnica=id_tecnica,
360
+ id_catador=id_catador,
361
+ )
362
+ return {r.id_producto.id: r for r in ratings}
363
+
364
+ @staticmethod
365
+ def getExistingPointsMap(ratings):
366
+ points = DatoPunto.objects.filter(calificacion__in=ratings)
367
+ return {p.calificacion.id: p for p in points}
tecnicas/controllers/api_controller/rating_pf_list_controller.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse, HttpRequest
2
+ from django.db import transaction, IntegrityError
3
+ from tecnicas.models import Palabra, ListaPalabras, Participacion, Calificacion, Producto, Dato, ValorDecimal
4
+ from tecnicas.forms import ListWordsForm
5
+
6
+
7
+ class RatingPFListController():
8
+ def __init__(self):
9
+ pass
10
+
11
+ @staticmethod
12
+ def getListWords(request: HttpRequest):
13
+ participation_current_tester = Participacion.objects.get(
14
+ id=request.session["id_participation"])
15
+
16
+ technique = participation_current_tester.tecnica
17
+
18
+ participations_testers = Participacion.objects.exclude(
19
+ catador=participation_current_tester.catador).filter(tecnica=technique)
20
+
21
+ all_testers = [
22
+ participation.catador for participation in participations_testers]
23
+
24
+ list_words_testers = list(ListaPalabras.objects.filter(
25
+ tecnica=technique, catador__in=all_testers, es_final=True))
26
+
27
+ if not list_words_testers:
28
+ return JsonResponse({"error": "Aun no hay listas finales de Catadores"})
29
+
30
+ result = []
31
+ for list_tester in list_words_testers:
32
+ try:
33
+ username = list_tester.catador.user.username
34
+ except Exception:
35
+ username = None
36
+
37
+ finish = [
38
+ participation.finalizado for participation in participations_testers if participation.catador.user.username == username][0]
39
+
40
+ status = "Lista terminada" if finish else "Lista en proceso"
41
+
42
+ words_qs = list_tester.palabras.all()
43
+ words = []
44
+ for p in words_qs:
45
+ nombre = getattr(p, 'nombre_palabra', None)
46
+ words.append({
47
+ 'id': getattr(p, 'id', None),
48
+ 'nombre_palabra': nombre
49
+ })
50
+
51
+ result.append({
52
+ 'username': username,
53
+ 'words': words,
54
+ 'status': status
55
+ })
56
+
57
+ return JsonResponse({
58
+ "message": "Listas encontradas",
59
+ "lists_words": result
60
+ })
61
+
62
+ @staticmethod
63
+ def saveList(request: HttpRequest, words: list, current_phase: int):
64
+ dic_words = {}
65
+ for index, word in enumerate(words, start=1):
66
+ dic_words[f"palabra_{index}"] = word
67
+
68
+ form = ListWordsForm(dic_words, new_words=words)
69
+
70
+ if form.is_valid():
71
+ participation = Participacion.objects.get(
72
+ id=request.session["id_participation"])
73
+
74
+ if not participation:
75
+ return JsonResponse({"error": "No está autorizado en la sesión"})
76
+
77
+ technique = participation.tecnica
78
+
79
+ list_words_tester: ListaPalabras
80
+ if current_phase == 1:
81
+ (list_words_tester, created) = ListaPalabras.objects.get_or_create(
82
+ tecnica=technique,
83
+ catador=request.user.user_catador,
84
+ es_final=False,
85
+ )
86
+ elif current_phase == 2:
87
+ (list_words_tester, created) = ListaPalabras.objects.get_or_create(
88
+ tecnica=technique,
89
+ catador=request.user.user_catador,
90
+ es_final=True,
91
+ )
92
+
93
+ added_words = RatingPFListController.addWordsToListWordsTester(
94
+ list_words=words, list_tester=list_words_tester)
95
+
96
+ response = JsonResponse({
97
+ "message": "Palabras guardadas con exito",
98
+ "words": [word.nombre_palabra for word in added_words]
99
+ })
100
+ else:
101
+ response = JsonResponse({"error": "Palabras invalidas"})
102
+
103
+ return response
104
+
105
+ @staticmethod
106
+ def addWordsToListWordsTester(list_words: list[str], list_tester: ListaPalabras):
107
+ # Normalizar
108
+ clean_words = [s.strip() for s in list_words if s.strip()]
109
+
110
+ # Obtener existentes
111
+ all_words = Palabra.objects.filter(nombre_palabra__in=clean_words)
112
+
113
+ names_words_exist = set(
114
+ all_words.values_list('nombre_palabra', flat=True))
115
+
116
+ # Ejecutar el query para no sumar palabras repetidas
117
+ all_words = list(all_words)
118
+
119
+ # Determinar faltantes
120
+ missing_words = [
121
+ nombre for nombre in clean_words if nombre not in names_words_exist]
122
+ print("No save words", missing_words)
123
+
124
+ created_words = []
125
+
126
+ # Intentar crear missing_words
127
+ for nombre in missing_words:
128
+ try:
129
+ with transaction.atomic():
130
+ palabra, created = Palabra.objects.get_or_create(
131
+ nombre_palabra=nombre)
132
+ if created:
133
+ created_words.append(palabra)
134
+ except IntegrityError:
135
+ palabra = Palabra.objects.get(nombre_palabra=nombre)
136
+ created_words.append(palabra)
137
+
138
+ # Combinar todas (all_words + created_words)
139
+ all_new_words = all_words + created_words
140
+
141
+ list_tester.palabras.set(all_new_words)
142
+
143
+ return all_new_words
144
+
145
+ @staticmethod
146
+ def saveRatings(request: HttpRequest, word_rating: str, data: list):
147
+ participation = Participacion.objects.get(
148
+ id=request.session["id_participation"])
149
+
150
+ technique = participation.tecnica
151
+ products = Producto.objects.filter(id_tecnica=technique)
152
+
153
+ # Crear o obtener las instancias Calificacion en la repeticion de todos los producutos
154
+ ratings = Calificacion.objects.filter(
155
+ num_repeticion=technique.repeticion,
156
+ id_tecnica=technique,
157
+ id_catador=participation.catador,
158
+ id_producto__in=products
159
+ )
160
+
161
+ existing_dict = {rating.id_producto_id: rating for rating in ratings}
162
+ to_create = []
163
+
164
+ for product in products:
165
+ if product.id not in existing_dict:
166
+ to_create.append(
167
+ Calificacion(
168
+ num_repeticion=technique.repeticion,
169
+ id_producto=product,
170
+ id_tecnica=technique,
171
+ id_catador=participation.catador
172
+ )
173
+ )
174
+ Calificacion.objects.bulk_create(to_create)
175
+
176
+ ratings = Calificacion.objects.filter(
177
+ num_repeticion=technique.repeticion,
178
+ id_tecnica=technique,
179
+ id_catador=participation.catador,
180
+ id_producto__in=products
181
+ )
182
+
183
+ # Guardar datos con ValorDecimal
184
+ word = Palabra.objects.get(nombre_palabra=word_rating)
185
+ try:
186
+ if (
187
+ len(data) != len(products) or
188
+ len(data) != len(ratings) or
189
+ len(products) != len(ratings)
190
+ ):
191
+ raise ValueError(
192
+ "Al parecer los datos mandados no corresponden con el total de productos")
193
+
194
+ product_values = {info["product"]["code"]: info["value"] for info in data}
195
+
196
+ datos_to_create = []
197
+ values_to_create = []
198
+
199
+ with transaction.atomic():
200
+ for rating in ratings:
201
+ datos_to_create.append(Dato(
202
+ id_palabra=word,
203
+ id_calificacion=rating
204
+ ))
205
+
206
+ datos_save = Dato.objects.bulk_create(datos_to_create)
207
+ save_data = Dato.objects.filter(id_palabra=word, id_calificacion__in=ratings)
208
+
209
+ for data_save in save_data:
210
+ values_to_create.append(ValorDecimal(
211
+ id_dato=data_save,
212
+ valor=product_values.get(data_save.id_calificacion.id_producto.codigoProducto)
213
+ ))
214
+
215
+ ValorDecimal.objects.bulk_create(values_to_create)
216
+
217
+ return JsonResponse({"message": "Calificaciones guardadas con exito"})
218
+ except ValueError as e:
219
+ error_message = str(e)
220
+ print(f"Error de calificacion: {error_message}")
221
+ return JsonResponse({"error": error_message})
tecnicas/controllers/{views_controller/api_rating_controller.py → api_controller/rating_sacales_controller.py} RENAMED
@@ -1,8 +1,8 @@
1
- from ...controllers import CalificacionController, DatoController
2
  from ...utils import controller_error
3
 
4
 
5
- class ApiRatingController():
6
  def __init__(self, rating_controller: CalificacionController, data_controller: DatoController):
7
  self.rating_controller = rating_controller
8
  self.data_controller = data_controller
 
1
+ from .. import CalificacionController, DatoController
2
  from ...utils import controller_error
3
 
4
 
5
+ class RatingScalesController():
6
  def __init__(self, rating_controller: CalificacionController, data_controller: DatoController):
7
  self.rating_controller = rating_controller
8
  self.data_controller = data_controller
tecnicas/controllers/api_controller/rating_sort_controller.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse, HttpRequest
2
+ from django.db import transaction
3
+ from tecnicas.models import Participacion, Palabra, GrupoProducto, Producto
4
+
5
+
6
+ class RatingSortController():
7
+ def __init__(self):
8
+ pass
9
+
10
+ @staticmethod
11
+ def saveRating(request: HttpRequest, data: list[dict]):
12
+ try:
13
+ with transaction.atomic():
14
+ participation = Participacion.objects.get(
15
+ id=request.session["id_participation"])
16
+
17
+ technique = participation.tecnica
18
+ catador = participation.catador
19
+
20
+ # Obtener productos de la técnica
21
+ technique_products = Producto.objects.filter(
22
+ id_tecnica=technique)
23
+ technique_product_ids = set(p.id for p in technique_products)
24
+
25
+ # Recolectar IDs de productos enviados
26
+ sent_product_ids = set()
27
+ for group in data:
28
+ for product in group["products"]:
29
+ sent_product_ids.add(int(product["id"]))
30
+
31
+ # Validar que los productos enviados existan en la técnica
32
+ if not sent_product_ids.issubset(technique_product_ids):
33
+ return JsonResponse({"error": "Productos enviados no pertenecen a la técnica"})
34
+
35
+ # Validar que todos los productos de la técnica estén presentes
36
+ if sent_product_ids != technique_product_ids:
37
+ return JsonResponse({"error": "Faltan productos por clasificar"})
38
+
39
+ for group in data:
40
+ words_data = group["words"]
41
+ products_data = group["products"]
42
+
43
+ # Crear u obtener palabras
44
+ words_objs = []
45
+ for word_name in words_data:
46
+ word, created = Palabra.objects.get_or_create(nombre_palabra=word_name)
47
+ words_objs.append(word)
48
+
49
+ # Crear GrupoProducto
50
+ group_product = GrupoProducto.objects.create(
51
+ tecnica=technique,
52
+ catador=catador
53
+ )
54
+
55
+ # Asignar palabras
56
+ group_product.palabras.set(words_objs)
57
+
58
+ # Asignar productos
59
+ product_ids = [p["id"] for p in products_data]
60
+ group_product.productos.set(product_ids)
61
+
62
+ return JsonResponse({"message": "Valores guardados"})
63
+ except Participacion.DoesNotExist:
64
+ return JsonResponse({"error": "Participación no encontrada"})
65
+ except Exception as e:
66
+ print(f"Error de calificacion: {e}")
67
+ return JsonResponse({"error": "Error al guardar los datos"})
tecnicas/controllers/models_controller/calificacion_controller.py CHANGED
@@ -21,7 +21,7 @@ class CalificacionController():
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
 
21
  @staticmethod
22
  def getRatingsByTechnique(technique: Tecnica):
23
  repetition = technique.repeticion
24
+ ratings = list(Calificacion.objects.filter(id_tecnica=technique, num_repeticion=repetition))
25
  return ratings
26
 
27
  @staticmethod
tecnicas/controllers/models_controller/dato_controller.py CHANGED
@@ -1,5 +1,5 @@
1
- from ...models import Calificacion, Dato, Palabra, ValorDecimal, ValorBooleano, Tecnica
2
- from ...utils import controller_error, getId
3
  from django.core.exceptions import ValidationError
4
  from django.db.models import F
5
 
@@ -12,11 +12,7 @@ class DatoController():
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,15 +35,23 @@ class DatoController():
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
 
53
  def saveValue(self):
@@ -94,3 +98,21 @@ class DatoController():
94
  )
95
 
96
  return list(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import Calificacion, Dato, Palabra, ValorDecimal, ValorBooleano, Tecnica, Catador
2
+ from tecnicas.utils import controller_error, getId
3
  from django.core.exceptions import ValidationError
4
  from django.db.models import F
5
 
 
12
  }
13
 
14
  self.data = Dato(**atributes)
15
+ self.value_rating = value_rating
 
 
 
 
16
 
17
  def setRating(self, new_rating: Calificacion):
18
  try:
 
35
  except ValidationError as e:
36
  return controller_error(e.message)
37
 
38
+ def setValue(self):
39
+ type_technique = self.data.id_calificacion.id_tecnica.tipo_tecnica
40
+ if type_technique == "cata":
41
+ self.value_data = ValorBooleano(valor=self.value_rating)
42
+
 
43
  else:
44
+ type_scale = self.data.id_calificacion.id_tecnica.escala_tecnica.id_tipo_escala.nombre_escala
45
+
46
+ if type_scale == "continua":
47
+ decimal_value = self.value_rating/100
48
+ value_rounded = round(decimal_value)
49
+ self.value_data = ValorDecimal(valor=value_rounded)
50
 
51
+ else:
52
+ self.value_data = ValorDecimal(valor=self.value_rating)
53
+
54
+ self.value_data.id_dato = self.data
55
  return self.value_data
56
 
57
  def saveValue(self):
 
98
  )
99
 
100
  return list(result)
101
+
102
+ @staticmethod
103
+ def getWordValuesPF(technique: Tecnica, ratings: list[Calificacion], tester: Catador):
104
+ ids_ratings = [rat.id for rat in ratings]
105
+
106
+ result = (
107
+ ValorDecimal.objects
108
+ .filter(id_dato__id_calificacion_id__in=ids_ratings, id_dato__id_calificacion__id_catador=tester)
109
+ .values(
110
+ nombre_palabra=F("id_dato__id_palabra__nombre_palabra"),
111
+ repeticion=F("id_dato__id_calificacion__num_repeticion"),
112
+ producto_code=F(
113
+ "id_dato__id_calificacion__id_producto__codigoProducto"),
114
+ dato_valor=F("valor")
115
+ )
116
+ )
117
+
118
+ return list(result)
tecnicas/controllers/models_controller/escala_controller.py CHANGED
@@ -7,19 +7,25 @@ class EscalaController():
7
  scale: Escala
8
  tags_relation: dict[str, EtiquetasEscala]
9
 
10
- def __init__(self, data):
11
- self.scale = Escala(
12
- id_tipo_escala=TipoEscala.objects.get(id=data["id_scale"]),
13
- longitud=data["size"],
14
- tecnica=data["technique"]
15
- )
16
-
17
- def setScale(self, newData):
18
- self.scale = Escala(
19
- id_tipo_escala=TipoEscala.objects.get(id=newData["id_scale"]),
20
- longitud=newData["size"],
21
- tecnica=newData["technique"]
22
- )
 
 
 
 
 
 
23
 
24
  def saveScale(self):
25
  try:
 
7
  scale: Escala
8
  tags_relation: dict[str, EtiquetasEscala]
9
 
10
+ def __init__(self, data, use_scale: Escala = None):
11
+ if use_scale:
12
+ self.scale = use_scale
13
+ else:
14
+ self.scale = Escala(
15
+ id_tipo_escala=TipoEscala.objects.get(id=data["id_scale"]),
16
+ longitud=data["size"],
17
+ tecnica=data["technique"]
18
+ )
19
+
20
+ def setScale(self, newData, use_scale: Escala):
21
+ if use_scale:
22
+ self.scale = use_scale
23
+ else:
24
+ self.scale = Escala(
25
+ id_tipo_escala=TipoEscala.objects.get(id=data["id_scale"]),
26
+ longitud=data["size"],
27
+ tecnica=data["technique"]
28
+ )
29
 
30
  def saveScale(self):
31
  try:
tecnicas/controllers/models_controller/particiapacion_controller.py CHANGED
@@ -35,24 +35,14 @@ class ParticipacionController():
35
  return controller_error("No se ha encontrado la participación")
36
 
37
  @staticmethod
38
- def outAllInSession(session: SesionSensorial | str):
39
  try:
40
- if isinstance(session, str):
41
- use_session = SesionSensorial.objects.get(
42
- codigo_sesion=session)
43
- else:
44
- use_session = session
45
-
46
  participations = Participacion.objects.filter(
47
- tecnica=use_session.tecnica)
48
-
49
- if not participations.exists():
50
- message = "No se encontraron participaciones en la sesión"
51
- return (False, message)
52
 
53
  participations.update(finalizado=False)
54
 
55
- message = "Participaciones actualizadas a finalizadas"
56
  return (True, message)
57
  except Exception as e:
58
  print(f"Error al actualizar las participaciones: {str(e)}")
 
35
  return controller_error("No se ha encontrado la participación")
36
 
37
  @staticmethod
38
+ def outAllInSession(session: SesionSensorial):
39
  try:
 
 
 
 
 
 
40
  participations = Participacion.objects.filter(
41
+ tecnica=session.tecnica)
 
 
 
 
42
 
43
  participations.update(finalizado=False)
44
 
45
+ message = "Participaciones actualizadas a finalizadas como falso"
46
  return (True, message)
47
  except Exception as e:
48
  print(f"Error al actualizar las participaciones: {str(e)}")
tecnicas/controllers/models_controller/sesion_controller.py CHANGED
@@ -125,12 +125,7 @@ class SesionController():
125
  return controller_error("Presentador invalido")
126
 
127
  @staticmethod
128
- def finishRepetion(session: SesionSensorial | str):
129
- if isinstance(session, str):
130
- use_session = SesionSensorial.objects.get(codigo_sesion=session)
131
- else:
132
- use_session = session
133
-
134
- use_session.activo = False
135
- use_session.save()
136
  return session
 
125
  return controller_error("Presentador invalido")
126
 
127
  @staticmethod
128
+ def finishRepetion(session: SesionSensorial):
129
+ session.activo = False
130
+ session.save()
 
 
 
 
 
131
  return session
tecnicas/controllers/models_controller/tecnica_controller.py CHANGED
@@ -16,7 +16,7 @@ class TecnicaController():
16
  def setTechniqueFromBasicData(self, basic):
17
  self.technique = Tecnica(
18
  tipo_tecnica=TipoTecnica.objects.get(nombre_tecnica=basic["name_tecnica"]),
19
- id_estilo=EstiloPalabra.objects.get(id=basic["estilo_palabras"]),
20
  repeticiones_max=basic["numero_repeticiones"] or 1,
21
  limite_catadores=basic["numero_catadores"],
22
  instrucciones=basic["instrucciones"] or "Espere instrucciones del Presentador",
 
16
  def setTechniqueFromBasicData(self, basic):
17
  self.technique = Tecnica(
18
  tipo_tecnica=TipoTecnica.objects.get(nombre_tecnica=basic["name_tecnica"]),
19
+ id_estilo=EstiloPalabra.objects.get(nombre_estilo=basic["estilo_palabras"]),
20
  repeticiones_max=basic["numero_repeticiones"] or 1,
21
  limite_catadores=basic["numero_catadores"],
22
  instrucciones=basic["instrucciones"] or "Espere instrucciones del Presentador",
tecnicas/controllers/views_controller/create_session/panel_basic_controller.py CHANGED
@@ -1,4 +1,4 @@
1
- from tecnicas.forms import SesionBasicForm, SesionBasicCATAForm
2
  from django.http import HttpRequest
3
  from django.shortcuts import redirect, render
4
  from django.urls import reverse
@@ -10,11 +10,14 @@ class PanelBasicController():
10
  "numero_repeticiones": 1
11
  }
12
 
13
- url_panel_basic = "tecnicas/create_sesion/configuracion-panel-basic.html"
14
  url_panel_basic_cata = "tecnicas/create_sesion/panel-basic-cata.html"
 
 
 
15
 
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
 
@@ -42,7 +45,7 @@ class PanelBasicController():
42
  if form.is_valid():
43
  values = {}
44
  for name, value in form.cleaned_data.items():
45
- if name == "estilo_palabras" or name == "tipo_escala":
46
  values[name] = value.id
47
  else:
48
  values[name] = value
@@ -50,7 +53,7 @@ class PanelBasicController():
50
  values["name_tecnica"] = name_tecnica
51
  request.session['form_basic'] = values
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"})
@@ -83,7 +86,7 @@ class PanelBasicController():
83
  if form.is_valid():
84
  values = {}
85
  for name, value in form.cleaned_data.items():
86
- if name == "estilo_palabras" or name == "tipo_escala":
87
  values[name] = value.id
88
  else:
89
  values[name] = value
@@ -102,7 +105,7 @@ class PanelBasicController():
102
  values["name_tecnica"] = name_tecnica
103
  request.session['form_basic'] = values
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"})
@@ -131,17 +134,107 @@ class PanelBasicController():
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.forms import SesionBasicForm, SesionBasicCATAForm, SesionBasicPFForm, SesionBasicSortForm, SesionBasicNappingForm
2
  from django.http import HttpRequest
3
  from django.shortcuts import redirect, render
4
  from django.urls import reverse
 
10
  "numero_repeticiones": 1
11
  }
12
 
13
+ url_panel_basic = "tecnicas/create_sesion/conf-panel-basic.html"
14
  url_panel_basic_cata = "tecnicas/create_sesion/panel-basic-cata.html"
15
+ url_panel_basic_pf = "tecnicas/create_sesion/panel-basic-pf.html"
16
+ url_panel_basic_sort = "tecnicas/create_sesion/panel-basic-sort.html"
17
+ url_panel_basic_napping = "tecnicas/create_sesion/panel-basic-napping.html"
18
 
19
+ url_next_panel_tags = "cata_system:panel_configuracion_tags"
20
+ url_next_panel_codes = "cata_system:panel_configuracion_codes"
21
 
22
  url_select_technique = "cata_system:seleccion_tecnica"
23
 
 
45
  if form.is_valid():
46
  values = {}
47
  for name, value in form.cleaned_data.items():
48
+ if name == "tipo_escala":
49
  values[name] = value.id
50
  else:
51
  values[name] = value
 
53
  values["name_tecnica"] = name_tecnica
54
  request.session['form_basic'] = values
55
  response = redirect(
56
+ reverse(PanelBasicController.url_next_panel_tags))
57
  else:
58
  response = render(request, PanelBasicController.url_panel_basic, {
59
  "form_sesion": form, "error": "Información no valida"})
 
86
  if form.is_valid():
87
  values = {}
88
  for name, value in form.cleaned_data.items():
89
+ if name == "tipo_escala":
90
  values[name] = value.id
91
  else:
92
  values[name] = value
 
105
  values["name_tecnica"] = name_tecnica
106
  request.session['form_basic'] = values
107
  response = redirect(
108
+ reverse(PanelBasicController.url_next_panel_tags))
109
  else:
110
  response = render(request, PanelBasicController.url_panel_basic, {
111
  "form_sesion": form, "error": "Información no valida"})
 
134
  if form.is_valid():
135
  values = {}
136
  for name, value in form.cleaned_data.items():
137
+ values[name] = value
 
 
 
138
 
139
  values["name_tecnica"] = name_tecnica
140
  request.session['form_basic'] = values
141
  response = redirect(
142
+ reverse(PanelBasicController.url_next_panel_codes))
143
  else:
144
  response = render(request, PanelBasicController.url_panel_basic, {
145
  "form_sesion": form, "error": "Información no valida"})
146
 
147
  return response
148
+
149
+ @staticmethod
150
+ def controllGetPF(request: HttpRequest):
151
+ form_sesion = SesionBasicPFForm()
152
+
153
+ view_context = {
154
+ "form_sesion": form_sesion,
155
+ "use_technique": "perfil flash"
156
+ }
157
+
158
+ return render(
159
+ request, PanelBasicController.url_panel_basic_pf, view_context)
160
+
161
+ @staticmethod
162
+ def controllPostPF(request: HttpRequest, name_tecnica: str):
163
+ form = SesionBasicPFForm(request.POST)
164
+
165
+ if form.is_valid():
166
+ values = {}
167
+ for name, value in form.cleaned_data.items():
168
+ values[name] = value
169
+
170
+ values["name_tecnica"] = name_tecnica
171
+ request.session['form_basic'] = values
172
+ response = redirect(
173
+ reverse(PanelBasicController.url_next_panel_codes))
174
+ else:
175
+ response = render(request, PanelBasicController.url_panel_basic_pf, {
176
+ "form_sesion": form, "error": "Información no valida"})
177
+
178
+ return response
179
+
180
+ @staticmethod
181
+ def controllGetSort(request: HttpRequest):
182
+ form_sesion = SesionBasicSortForm()
183
+
184
+ view_context = {
185
+ "form_sesion": form_sesion,
186
+ "use_technique": "sort"
187
+ }
188
+
189
+ return render(
190
+ request, PanelBasicController.url_panel_basic_sort, view_context)
191
+
192
+ @staticmethod
193
+ def controllPostSort(request: HttpRequest, name_tecnica: str):
194
+ form = SesionBasicSortForm(request.POST)
195
+
196
+ if form.is_valid():
197
+ values = {}
198
+ for name, value in form.cleaned_data.items():
199
+ values[name] = value
200
+
201
+ values["name_tecnica"] = name_tecnica
202
+ request.session['form_basic'] = values
203
+ response = redirect(
204
+ reverse(PanelBasicController.url_next_panel_codes))
205
+ else:
206
+ response = render(request, PanelBasicController.url_panel_basic_sort, {
207
+ "form_sesion": form, "error": "Información no valida"})
208
+
209
+ return response
210
+
211
+ @staticmethod
212
+ def controllGetNapping(request: HttpRequest):
213
+ form_sesion = SesionBasicNappingForm()
214
+
215
+ view_context = {
216
+ "form_sesion": form_sesion,
217
+ "use_technique": "napping"
218
+ }
219
+
220
+ return render(
221
+ request, PanelBasicController.url_panel_basic_napping, view_context)
222
+
223
+ @staticmethod
224
+ def controllPostNapping(request: HttpRequest, name_tecnica: str):
225
+ form = SesionBasicNappingForm(request.POST)
226
+
227
+ if form.is_valid():
228
+ values = {}
229
+ for name, value in form.cleaned_data.items():
230
+ values[name] = value
231
+
232
+ values["name_tecnica"] = name_tecnica
233
+ request.session['form_basic'] = values
234
+ response = redirect(
235
+ reverse(PanelBasicController.url_next_panel_codes))
236
+ else:
237
+ response = render(request, PanelBasicController.url_panel_basic_napping, {
238
+ "form_sesion": form, "error": "Información no valida"})
239
+
240
+ return response
tecnicas/controllers/views_controller/create_session/panel_codes_controller.py CHANGED
@@ -7,8 +7,9 @@ import json
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
@@ -58,14 +59,14 @@ class PanelCodesController():
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):
69
  num_products = data["numero_productos"]
70
  codes_products = generarCodigos(num_products)
71
  form_codes = CodesForm(codes=codes_products)
@@ -73,13 +74,13 @@ class PanelCodesController():
73
  context_codes_form = {
74
  "form_codes": form_codes,
75
  "num_tester": 0,
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,12 +92,14 @@ class PanelCodesController():
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
 
@@ -114,3 +117,29 @@ class PanelCodesController():
114
  }
115
 
116
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
 
9
  class PanelCodesController():
10
+ url_current_panel = "tecnicas/create_sesion/conf-panel-codes.html"
11
+ url_words = "cata_system:panel_configuracion_words"
12
+ url_create_session = "cata_system:creando_sesion"
13
 
14
  def __init__(self):
15
  pass
 
59
 
60
  codes_sort["sort_codes"] = sorts_code
61
  request.session["form_codes"] = codes_sort
62
+ return redirect(reverse(PanelCodesController.url_words))
63
  else:
64
  context_codes_form["error"] = "error en los datos recibidos"
65
 
66
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
67
 
68
  @staticmethod
69
+ def controllGetWithoutOrders(request: HttpRequest, data, name_technique: str):
70
  num_products = data["numero_productos"]
71
  codes_products = generarCodigos(num_products)
72
  form_codes = CodesForm(codes=codes_products)
 
74
  context_codes_form = {
75
  "form_codes": form_codes,
76
  "num_tester": 0,
77
+ "use_technique": name_technique
78
  }
79
 
80
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
81
 
82
  @staticmethod
83
+ def controllPostWithWords(request: HttpRequest, name_technique: str):
84
  codes = []
85
  context_codes_form = {}
86
 
 
92
 
93
  context_codes_form = {
94
  "form_codes": form_codes,
95
+ "use_technique": name_technique
96
  }
97
 
98
  if form_codes.is_valid():
99
+ # Extract codes from cleaned_data to ensure uppercase conversion
100
+ cleaned_codes = [value for name, value in form_codes.cleaned_data.items() if name.startswith('producto_')]
101
+ request.session["form_codes"] = cleaned_codes
102
+ return redirect(reverse(PanelCodesController.url_words))
103
  else:
104
  context_codes_form["error"] = "error en los datos recibidos"
105
 
 
117
  }
118
 
119
  return render(request, PanelCodesController.url_current_panel, context_codes_form)
120
+
121
+ @staticmethod
122
+ def controllPostWithoutOrdersWords(request: HttpRequest, name_technique: str):
123
+ codes = []
124
+ context_codes_form = {}
125
+
126
+ for name, value in request.POST.items():
127
+ if name.__contains__("producto_"):
128
+ codes.append(value)
129
+
130
+ form_codes = CodesForm(request.POST, codes=codes)
131
+
132
+ context_codes_form = {
133
+ "form_codes": form_codes,
134
+ "use_technique": name_technique
135
+ }
136
+
137
+ if form_codes.is_valid():
138
+ # Extract codes from cleaned_data to ensure uppercase conversion
139
+ cleaned_codes = [value for name, value in form_codes.cleaned_data.items() if name.startswith('producto_')]
140
+ request.session["form_codes"] = cleaned_codes
141
+ return redirect(reverse(PanelCodesController.url_create_session))
142
+ else:
143
+ context_codes_form["error"] = "error en los datos recibidos"
144
+
145
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
tecnicas/controllers/views_controller/create_session/panel_tags_controller.py CHANGED
@@ -25,7 +25,7 @@ class PanelTagsController():
25
  "form_new_tag": form_new_etiqueta
26
  }
27
 
28
- return render(request, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
29
 
30
  @staticmethod
31
  def controllPostEscalas(request: HttpRequest, data):
@@ -54,7 +54,7 @@ class PanelTagsController():
54
  else:
55
  context_tags["error"] = "ha ocurrido un error"
56
  response = render(
57
- request, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
58
 
59
  return response
60
 
 
25
  "form_new_tag": form_new_etiqueta
26
  }
27
 
28
+ return render(request, "tecnicas/create_sesion/conf-panel-tags.html", context_tags)
29
 
30
  @staticmethod
31
  def controllPostEscalas(request: HttpRequest, data):
 
54
  else:
55
  context_tags["error"] = "ha ocurrido un error"
56
  response = render(
57
+ request, "tecnicas/create_sesion/conf-panel-tags.html", context_tags)
58
 
59
  return response
60
 
tecnicas/controllers/views_controller/create_session/panel_words_controller.py CHANGED
@@ -7,7 +7,7 @@ import json
7
 
8
 
9
  class PanelWordsController():
10
- current_url_escalas_atribute = "tecnicas/create_sesion/configuracion-panel-words.html"
11
  current_url_escalas_vocabulary = "tecnicas/create_sesion/conf-panel-vocabulary.html"
12
 
13
  def __init__(self):
 
7
 
8
 
9
  class PanelWordsController():
10
+ current_url_escalas_atribute = "tecnicas/create_sesion/conf-panel-words.html"
11
  current_url_escalas_vocabulary = "tecnicas/create_sesion/conf-panel-vocabulary.html"
12
 
13
  def __init__(self):
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_cata_controller.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.models import EsVocabulario, Tecnica, TipoTecnica, EstiloPalabra, EsAtributo, Vocabulario, Palabra, SesionSensorial, Producto
5
+ from tecnicas.utils import deleteDataSession, general_error
6
+
7
+
8
+ class PanelCreateCataController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_codes") or not request.session.get("form_words"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # //////////////////////////// #
21
+ #
22
+ # First step: Create technique #
23
+ #
24
+ # //////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ data_basic["numero_catadores"] = 0
27
+ data_basic["numero_repeticiones"] = 1
28
+
29
+ technique = Tecnica.objects.create(
30
+ tipo_tecnica=TipoTecnica.objects.get(
31
+ nombre_tecnica=data_basic["name_tecnica"]),
32
+ id_estilo=EstiloPalabra.objects.get(
33
+ nombre_estilo=data_basic["estilo_palabras"]),
34
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ # ////////////////////////////////////////////// #
43
+ #
44
+ # Second step: Create productos with their codes #
45
+ #
46
+ # ////////////////////////////////////////////// #
47
+ codes = request.session["form_codes"]
48
+
49
+ if not codes:
50
+ raise ValueError("No hay códigos de productos")
51
+
52
+ products_without_save = []
53
+ for code in codes:
54
+ product = Producto(
55
+ codigoProducto=code,
56
+ id_tecnica=technique
57
+ )
58
+ products_without_save.append(product)
59
+
60
+ Producto.objects.bulk_create(products_without_save)
61
+
62
+ # /////////////////////////////////////////////////////// #
63
+ #
64
+ # Third step: Create relations technique with Words Style #
65
+ #
66
+ # /////////////////////////////////////////////////////// #
67
+ style_words = technique.id_estilo.nombre_estilo
68
+
69
+ if style_words == "atributos":
70
+ raw_ids_words = request.session["form_words"]
71
+ ids_words = [int(id_w) for id_w in raw_ids_words]
72
+
73
+ words = Palabra.objects.filter(id__in=ids_words)
74
+
75
+ style_atribute = EsAtributo.objects.create(
76
+ id_tecnica=technique
77
+ )
78
+
79
+ if not style_atribute:
80
+ raise ValueError(
81
+ "Error al intentar relacionar las palabras con la técnica")
82
+
83
+ style_atribute.palabras.set(words)
84
+
85
+ elif style_words == "vocabulario":
86
+ name_vocabulary = request.session["form_words"]
87
+ try:
88
+ vocabulary = Vocabulario.objects.get(
89
+ nombre_vocabulario=name_vocabulary)
90
+ except Vocabulario.DoesNotExist:
91
+ raise ValueError("Vocabulario no encontrado")
92
+
93
+ es_vocabulary = EsVocabulario.objects.create(
94
+ id_tecnica=technique,
95
+ id_vocabulario=vocabulary
96
+ )
97
+ if not es_vocabulary:
98
+ raise ValueError(
99
+ "Error al intentar relacionar el vocabulario con la técnica")
100
+
101
+ else:
102
+ raise ValueError("Estilo de palabas no permitido")
103
+
104
+ # //////////////////////////////////////////////////////// #
105
+ #
106
+ # Fourth step: Create session and relat with the technique #
107
+ #
108
+ # //////////////////////////////////////////////////////// #
109
+ session = SesionSensorial.objects.create(
110
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
111
+ tecnica=technique,
112
+ creadoPor=request.user.user_presentador
113
+ )
114
+
115
+ if not session:
116
+ raise ValueError("Error al crear sesion sensorial")
117
+
118
+ context = {
119
+ "message": "sesión creada",
120
+ "data": {
121
+ "codigo_sesion": session.codigo_sesion,
122
+ "nombre_sesion": session.nombre_sesion
123
+ }
124
+ }
125
+
126
+ # ////////////////////////////////// #
127
+ #
128
+ # Final step: Delete date en session #
129
+ #
130
+ # ////////////////////////////////// #
131
+ deleteDataSession(request)
132
+ return JsonResponse(context)
133
+
134
+ except ValueError as e:
135
+ return general_error(f"Error: {e}")
136
+ else:
137
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_controller.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse, HttpRequest
2
+ from django.shortcuts import render
3
+
4
+ class PanelCreateController():
5
+ url_template = 'tecnicas/create_sesion/creating_session.html'
6
+
7
+ def __init__(self):
8
+ pass
9
+
10
+ @staticmethod
11
+ def controllGet(request: HttpRequest):
12
+ return render(
13
+ request, PanelCreateController.url_template)
14
+
15
+ @staticmethod
16
+ def controllPost(request: HttpRequest):
17
+ return JsonResponse({"message": "Método no permitido"})
tecnicas/controllers/views_controller/create_session/{panel_create_controller.py → panels_create/panel_create_escalas_controller.py} RENAMED
@@ -1,23 +1,17 @@
 
1
  from django.http import HttpRequest, JsonResponse
2
  from django.db import transaction
3
- from django.shortcuts import render
4
- from tecnicas.utils import general_error
5
- from tecnicas.models import EsAtributo, EsVocabulario, Vocabulario, Tecnica, TipoTecnica, EstiloPalabra, Producto, Palabra, SesionSensorial
6
- from tecnicas.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, EstiloPalabrasController, PalabrasController, SesionController
7
- from tecnicas.utils import deleteDataSession
8
 
9
 
10
- class PanelCreateController():
11
  def __init__(self):
12
- pass
13
 
14
  @staticmethod
15
- def controllGetEscalas(request: HttpRequest):
16
- return render(
17
- request, 'tecnicas/create_sesion/creando_sesion.html')
18
-
19
- @staticmethod
20
- def controllPostEscalas(request: HttpRequest):
21
  if request.POST.get('action') == 'create_session':
22
  if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
23
  deleteDataSession(request)
@@ -173,274 +167,3 @@ class PanelCreateController():
173
  return general_error(f"Error: {e}")
174
  else:
175
  return general_error("No se ha establecido acción")
176
-
177
- @staticmethod
178
- def controllPostRATA(request: HttpRequest):
179
- if request.POST.get('action') == 'create_session':
180
- if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
181
- deleteDataSession(request)
182
- return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
183
- try:
184
- with transaction.atomic():
185
- # ////////////////////////////////////////////////////// #
186
- #
187
- # First step: Create technique and scale with their tags #
188
- #
189
- # ////////////////////////////////////////////////////// #
190
- data_basic = request.session["form_basic"]
191
- data_basic["numero_catadores"] = 0
192
- data_basic["numero_repeticiones"] = 1
193
-
194
- technique = Tecnica.objects.create(
195
- tipo_tecnica=TipoTecnica.objects.get(
196
- nombre_tecnica=data_basic["name_tecnica"]),
197
- id_estilo=EstiloPalabra.objects.get(
198
- id=data_basic["estilo_palabras"]),
199
- repeticiones_max=data_basic["numero_repeticiones"] or 1,
200
- limite_catadores=data_basic["numero_catadores"],
201
- instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
202
- )
203
-
204
- if not technique:
205
- raise ValueError("Error al guardar la técnica")
206
-
207
- data_scale = {
208
- "id_scale": data_basic["tipo_escala"],
209
- "size": data_basic["tamano_escala"],
210
- "technique": technique
211
- }
212
-
213
- controllerScale = EscalaController(data=data_scale)
214
-
215
- scale = controllerScale.saveScale()
216
- if isinstance(scale, dict):
217
- raise ValueError(scale["error"])
218
-
219
- dict_tags = request.session["form_tags"]
220
- saved_related_tags = controllerScale.realteTags(dict_tags)
221
- if "error" in saved_related_tags:
222
- raise ValueError(saved_related_tags["error"])
223
-
224
- # ////////////////////////////////////////////// #
225
- #
226
- # Second step: Create productos with their codes #
227
- #
228
- # ////////////////////////////////////////////// #
229
- codes = request.session["form_codes"]
230
-
231
- if not codes:
232
- raise ValueError("No hay códigos de productos")
233
-
234
- products_without_save = []
235
- for code in codes:
236
- product = Producto(
237
- codigoProducto=code,
238
- id_tecnica=technique
239
- )
240
- products_without_save.append(product)
241
-
242
- Producto.objects.bulk_create(products_without_save)
243
-
244
- # /////////////////////////////////////////////////////// #
245
- #
246
- # Third step: Create relations technique with Words Style #
247
- #
248
- # /////////////////////////////////////////////////////// #
249
- style_words = technique.id_estilo.nombre_estilo
250
-
251
- if style_words == "atributos":
252
- raw_ids_words = request.session["form_words"]
253
- ids_words = [int(id_w) for id_w in raw_ids_words]
254
-
255
- words = Palabra.objects.filter(id__in=ids_words)
256
-
257
- style_atribute = EsAtributo.objects.create(
258
- id_tecnica=technique
259
- )
260
-
261
- if not style_atribute:
262
- raise ValueError(
263
- "Error al intentar relacionar las palabras con la técnica")
264
-
265
- style_atribute.palabras.set(words)
266
-
267
- elif style_words == "vocabulario":
268
- name_vocabulary = request.session["form_words"]
269
- try:
270
- vocabulary = Vocabulario.objects.get(
271
- nombre_vocabulario=name_vocabulary)
272
- except Vocabulario.DoesNotExist:
273
- raise ValueError("Vocabulario no encontrado")
274
-
275
- es_vocabulary = EsVocabulario.objects.create(
276
- id_tecnica=technique,
277
- id_vocabulario=vocabulary
278
- )
279
- if not es_vocabulary:
280
- raise ValueError(
281
- "Error al intentar relacionar el vocabulario con la técnica")
282
-
283
- else:
284
- raise ValueError("Estilo de palabas no permitido")
285
-
286
- # //////////////////////////////////////////////////////// #
287
- #
288
- # Fourth step: Create session and relat with the technique #
289
- #
290
- # //////////////////////////////////////////////////////// #
291
- session = SesionSensorial.objects.create(
292
- 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")
 
1
+ from .panel_create_controller import PanelCreateController
2
  from django.http import HttpRequest, JsonResponse
3
  from django.db import transaction
4
+ from tecnicas.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, PalabrasController, EstiloPalabrasController, SesionController
5
+ from tecnicas.models import EsVocabulario, Vocabulario
6
+ from tecnicas.utils import deleteDataSession, general_error
 
 
7
 
8
 
9
+ class PanelCreateEscalasController(PanelCreateController):
10
  def __init__(self):
11
+ super().__init__()
12
 
13
  @staticmethod
14
+ def controllPost(request: HttpRequest):
 
 
 
 
 
15
  if request.POST.get('action') == 'create_session':
16
  if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
17
  deleteDataSession(request)
 
167
  return general_error(f"Error: {e}")
168
  else:
169
  return general_error("No se ha establecido acción")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_napping_controller.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from tecnicas.models import Tecnica, TipoTecnica, EstiloPalabra, Producto, SesionSensorial, Modalidad, TecnicaModalidad
4
+ from django.db import transaction
5
+ from tecnicas.utils import deleteDataSession
6
+
7
+
8
+ class PanelCreateNappingController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_basic") or not request.session.get("form_codes"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # //////////////////////////// #
21
+ #
22
+ # First step: Create technique #
23
+ #
24
+ # //////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ data_basic["numero_catadores"] = data_basic["numero_catadores"] or 2
27
+ data_basic["numero_repeticiones"] = 0
28
+
29
+ technique = Tecnica.objects.create(
30
+ tipo_tecnica=TipoTecnica.objects.get(
31
+ nombre_tecnica=data_basic["name_tecnica"]),
32
+ id_estilo=EstiloPalabra.objects.get(
33
+ nombre_estilo="napping"),
34
+ repeticiones_max=data_basic["numero_repeticiones"],
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ # ////////////////////////////////////////////// #
43
+ #
44
+ # Second step: Create productos with their codes #
45
+ #
46
+ # ////////////////////////////////////////////// #
47
+ codes = request.session["form_codes"]
48
+
49
+ if not codes:
50
+ raise ValueError("No hay códigos de productos")
51
+
52
+ products_without_save = []
53
+ for code in codes:
54
+ product = Producto(
55
+ codigoProducto=code,
56
+ id_tecnica=technique
57
+ )
58
+ products_without_save.append(product)
59
+
60
+ Producto.objects.bulk_create(products_without_save)
61
+
62
+ # /////////////////////////////////////////////////////// #
63
+ #
64
+ # Third step: Create session and relat with the technique #
65
+ #
66
+ # /////////////////////////////////////////////////////// #
67
+ mod = Modalidad.objects.get(
68
+ nombre=data_basic["modalidad"])
69
+
70
+ if not mod:
71
+ raise ValueError("Modalidad no encontrada")
72
+
73
+ technique_mod = TecnicaModalidad.objects.create(
74
+ tecnica=technique,
75
+ modalidad=mod
76
+ )
77
+
78
+ if not technique_mod:
79
+ raise ValueError("Error al guardar la técnica")
80
+
81
+ # /////////////////////////////////////////////////////// #
82
+ #
83
+ # Fourth step: Create session and relat with the technique #
84
+ #
85
+ # /////////////////////////////////////////////////////// #
86
+ session = SesionSensorial.objects.create(
87
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
88
+ tecnica=technique,
89
+ creadoPor=request.user.user_presentador
90
+ )
91
+
92
+ if not session:
93
+ raise ValueError("Error al crear sesion sensorial")
94
+
95
+ context = {
96
+ "message": "sesión creada",
97
+ "data": {
98
+ "codigo_sesion": session.codigo_sesion,
99
+ "nombre_sesion": session.nombre_sesion
100
+ }
101
+ }
102
+
103
+ # ////////////////////////////////// #
104
+ #
105
+ # Final step: Delete date en session #
106
+ #
107
+ # ////////////////////////////////// #
108
+ deleteDataSession(request)
109
+ return JsonResponse(context)
110
+
111
+ except ValueError as e:
112
+ return general_error(f"Error: {e}")
113
+ else:
114
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_pf_controller.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.models import Tecnica, TipoTecnica, EstiloPalabra, SesionSensorial, Escala, TipoEscala, Producto
5
+ from tecnicas.utils import deleteDataSession, general_error
6
+
7
+
8
+ class PanelCreatePFController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_codes"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # ////////////////////////////////////// #
21
+ #
22
+ # First step: Create technique and scale #
23
+ #
24
+ # ////////////////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ phases_before_reptition = 2
27
+
28
+ technique = Tecnica.objects.create(
29
+ tipo_tecnica=TipoTecnica.objects.get(
30
+ nombre_tecnica=data_basic["name_tecnica"]),
31
+ id_estilo=EstiloPalabra.objects.get(
32
+ nombre_estilo="perfil flash"),
33
+ repeticiones_max=data_basic["numero_repeticiones"] +
34
+ phases_before_reptition,
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Analista",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ created_scale = Escala.objects.create(
43
+ id_tipo_escala=TipoEscala.objects.get(
44
+ nombre_escala="estructurada"),
45
+ longitud=data_basic["numero_productos"],
46
+ tecnica=technique
47
+ )
48
+
49
+ if not created_scale:
50
+ raise ValueError("No se ha podido crear la escala")
51
+
52
+ # ////////////////////////////////////////////// #
53
+ #
54
+ # Second step: Create productos with their codes #
55
+ #
56
+ # ////////////////////////////////////////////// #
57
+ codes = request.session["form_codes"]
58
+
59
+ if not codes:
60
+ raise ValueError("No hay códigos de productos")
61
+
62
+ products_without_save = []
63
+ for code in codes:
64
+ product = Producto(
65
+ codigoProducto=code,
66
+ id_tecnica=technique
67
+ )
68
+ products_without_save.append(product)
69
+
70
+ Producto.objects.bulk_create(products_without_save)
71
+
72
+ # /////////////////////////////////////////////////////// #
73
+ #
74
+ # Third step: Create session and relat with the technique #
75
+ #
76
+ # /////////////////////////////////////////////////////// #
77
+ session = SesionSensorial.objects.create(
78
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
79
+ tecnica=technique,
80
+ creadoPor=request.user.user_presentador
81
+ )
82
+
83
+ if not session:
84
+ raise ValueError("Error al crear sesion sensorial")
85
+
86
+ context = {
87
+ "message": "sesión creada",
88
+ "data": {
89
+ "codigo_sesion": session.codigo_sesion,
90
+ "nombre_sesion": session.nombre_sesion
91
+ }
92
+ }
93
+
94
+ # ////////////////////////////////// #
95
+ #
96
+ # Final step: Delete date en session #
97
+ #
98
+ # ////////////////////////////////// #
99
+ deleteDataSession(request)
100
+ return JsonResponse(context)
101
+
102
+ except ValueError as e:
103
+ return general_error(f"Error: {e}")
104
+ else:
105
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_rata_controller.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.controllers import EscalaController
5
+ from tecnicas.models import EsVocabulario, Tecnica, TipoTecnica, EstiloPalabra, EsAtributo, Vocabulario, Palabra, SesionSensorial, Producto
6
+ from tecnicas.utils import deleteDataSession, general_error
7
+
8
+
9
+ class PanelCreateRataController(PanelCreateController):
10
+ def __init__(self):
11
+ super().__init__()
12
+
13
+ @staticmethod
14
+ def controllPost(request: HttpRequest):
15
+ if request.POST.get('action') == 'create_session':
16
+ if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
17
+ deleteDataSession(request)
18
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
19
+ try:
20
+ with transaction.atomic():
21
+ # ////////////////////////////////////////////////////// #
22
+ #
23
+ # First step: Create technique and scale with their tags #
24
+ #
25
+ # ////////////////////////////////////////////////////// #
26
+ data_basic = request.session["form_basic"]
27
+ data_basic["numero_catadores"] = 0
28
+ data_basic["numero_repeticiones"] = 1
29
+
30
+ technique = Tecnica.objects.create(
31
+ tipo_tecnica=TipoTecnica.objects.get(
32
+ nombre_tecnica=data_basic["name_tecnica"]),
33
+ id_estilo=EstiloPalabra.objects.get(
34
+ nombre_estilo=data_basic["estilo_palabras"]),
35
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
36
+ limite_catadores=data_basic["numero_catadores"],
37
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
38
+ )
39
+
40
+ if not technique:
41
+ raise ValueError("Error al guardar la técnica")
42
+
43
+ data_scale = {
44
+ "id_scale": data_basic["tipo_escala"],
45
+ "size": data_basic["tamano_escala"],
46
+ "technique": technique
47
+ }
48
+
49
+ controllerScale = EscalaController(data=data_scale)
50
+
51
+ scale = controllerScale.saveScale()
52
+ if isinstance(scale, dict):
53
+ raise ValueError(scale["error"])
54
+
55
+ dict_tags = request.session["form_tags"]
56
+ saved_related_tags = controllerScale.realteTags(dict_tags)
57
+ if "error" in saved_related_tags:
58
+ raise ValueError(saved_related_tags["error"])
59
+
60
+ # ////////////////////////////////////////////// #
61
+ #
62
+ # Second step: Create productos with their codes #
63
+ #
64
+ # ////////////////////////////////////////////// #
65
+ codes = request.session["form_codes"]
66
+
67
+ if not codes:
68
+ raise ValueError("No hay códigos de productos")
69
+
70
+ products_without_save = []
71
+ for code in codes:
72
+ product = Producto(
73
+ codigoProducto=code,
74
+ id_tecnica=technique
75
+ )
76
+ products_without_save.append(product)
77
+
78
+ Producto.objects.bulk_create(products_without_save)
79
+
80
+ # /////////////////////////////////////////////////////// #
81
+ #
82
+ # Third step: Create relations technique with Words Style #
83
+ #
84
+ # /////////////////////////////////////////////////////// #
85
+ style_words = technique.id_estilo.nombre_estilo
86
+
87
+ if style_words == "atributos":
88
+ raw_ids_words = request.session["form_words"]
89
+ ids_words = [int(id_w) for id_w in raw_ids_words]
90
+
91
+ words = Palabra.objects.filter(id__in=ids_words)
92
+
93
+ style_atribute = EsAtributo.objects.create(
94
+ id_tecnica=technique
95
+ )
96
+
97
+ if not style_atribute:
98
+ raise ValueError(
99
+ "Error al intentar relacionar las palabras con la técnica")
100
+
101
+ style_atribute.palabras.set(words)
102
+
103
+ elif style_words == "vocabulario":
104
+ name_vocabulary = request.session["form_words"]
105
+ try:
106
+ vocabulary = Vocabulario.objects.get(
107
+ nombre_vocabulario=name_vocabulary)
108
+ except Vocabulario.DoesNotExist:
109
+ raise ValueError("Vocabulario no encontrado")
110
+
111
+ es_vocabulary = EsVocabulario.objects.create(
112
+ id_tecnica=technique,
113
+ id_vocabulario=vocabulary
114
+ )
115
+ if not es_vocabulary:
116
+ raise ValueError(
117
+ "Error al intentar relacionar el vocabulario con la técnica")
118
+
119
+ else:
120
+ raise ValueError("Estilo de palabas no permitido")
121
+
122
+ # //////////////////////////////////////////////////////// #
123
+ #
124
+ # Fourth step: Create session and relat with the technique #
125
+ #
126
+ # //////////////////////////////////////////////////////// #
127
+ session = SesionSensorial.objects.create(
128
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else "",
129
+ tecnica=technique,
130
+ creadoPor=request.user.user_presentador
131
+ )
132
+
133
+ if not session:
134
+ raise ValueError("Error al crear sesion sensorial")
135
+
136
+ context = {
137
+ "message": "sesión creada",
138
+ "data": {
139
+ "codigo_sesion": session.codigo_sesion,
140
+ "nombre_sesion": session.nombre_sesion
141
+ }
142
+ }
143
+
144
+ # ////////////////////////////////// #
145
+ #
146
+ # Final step: Delete date en session #
147
+ #
148
+ # ////////////////////////////////// #
149
+
150
+ deleteDataSession(request)
151
+ return JsonResponse(context)
152
+ except ValueError as e:
153
+ return general_error(f"Error: {e}")
154
+ else:
155
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/create_session/panels_create/panel_create_sort_controller.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .panel_create_controller import PanelCreateController
2
+ from django.http import HttpRequest, JsonResponse
3
+ from django.db import transaction
4
+ from tecnicas.models import Tecnica, TipoTecnica, EstiloPalabra, SesionSensorial, Producto
5
+ from tecnicas.utils import deleteDataSession, general_error
6
+
7
+
8
+ class PanelCreateSortController(PanelCreateController):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ @staticmethod
13
+ def controllPost(request: HttpRequest):
14
+ if request.POST.get('action') == 'create_session':
15
+ if not request.session.get("form_basic") or not request.session.get("form_codes"):
16
+ deleteDataSession(request)
17
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
18
+ try:
19
+ with transaction.atomic():
20
+ # //////////////////////////// #
21
+ #
22
+ # First step: Create technique #
23
+ #
24
+ # //////////////////////////// #
25
+ data_basic = request.session["form_basic"]
26
+ data_basic["numero_catadores"] = data_basic["numero_catadores"] or 1
27
+ data_basic["numero_repeticiones"] = 1
28
+
29
+ technique = Tecnica.objects.create(
30
+ tipo_tecnica=TipoTecnica.objects.get(
31
+ nombre_tecnica=data_basic["name_tecnica"]),
32
+ id_estilo=EstiloPalabra.objects.get(
33
+ nombre_estilo="sort"),
34
+ repeticiones_max=data_basic["numero_repeticiones"],
35
+ limite_catadores=data_basic["numero_catadores"],
36
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
37
+ )
38
+
39
+ if not technique:
40
+ raise ValueError("Error al guardar la técnica")
41
+
42
+ # ////////////////////////////////////////////// #
43
+ #
44
+ # Second step: Create productos with their codes #
45
+ #
46
+ # ////////////////////////////////////////////// #
47
+ codes = request.session["form_codes"]
48
+
49
+ if not codes:
50
+ raise ValueError("No hay códigos de productos")
51
+
52
+ products_without_save = []
53
+ for code in codes:
54
+ product = Producto(
55
+ codigoProducto=code,
56
+ id_tecnica=technique
57
+ )
58
+ products_without_save.append(product)
59
+
60
+ Producto.objects.bulk_create(products_without_save)
61
+
62
+ # /////////////////////////////////////////////////////// #
63
+ #
64
+ # Third step: Create session and relat with the technique #
65
+ #
66
+ # /////////////////////////////////////////////////////// #
67
+ session = SesionSensorial.objects.create(
68
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
69
+ tecnica=technique,
70
+ creadoPor=request.user.user_presentador
71
+ )
72
+
73
+ if not session:
74
+ raise ValueError("Error al crear sesion sensorial")
75
+
76
+ context = {
77
+ "message": "sesión creada",
78
+ "data": {
79
+ "codigo_sesion": session.codigo_sesion,
80
+ "nombre_sesion": session.nombre_sesion
81
+ }
82
+ }
83
+
84
+ # ////////////////////////////////// #
85
+ #
86
+ # Final step: Delete date en session #
87
+ #
88
+ # ////////////////////////////////// #
89
+ deleteDataSession(request)
90
+ return JsonResponse(context)
91
+
92
+ except ValueError as e:
93
+ return general_error(f"Error: {e}")
94
+ else:
95
+ return general_error("No se ha establecido acción")
tecnicas/controllers/views_controller/session_management/{details_rata_controller.py → details/details_cata_controller.py} RENAMED
@@ -1,48 +1,60 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import render, redirect
3
- from tecnicas.models import SesionSensorial
4
- from tecnicas.controllers import PalabrasController, DatoController, CalificacionController
5
  from tecnicas.utils import defaultdict_to_dict
6
  from .details_controller import DetallesController
7
  from collections import defaultdict
8
 
9
 
10
- class DetallesRATAController(DetallesController):
11
  def __init__(self, session: SesionSensorial):
12
  super().__init__(session)
13
-
14
- def getResponse(self, request: HttpRequest, error: str = ""):
15
- context = self.getContext()
16
- if error != "" or error:
17
- context["error"] = error
18
- return render(
19
- request, self.url_template, context)
20
 
21
  def getContext(self):
22
- self.context = {}
23
- self.context["sesion"] = self.session
 
 
 
 
 
 
24
  self.words = PalabrasController.getWordsInTechnique(
25
  self.session.tecnica)
26
  self.context["palabras"] = [word.nombre_palabra for word in self.words]
27
 
 
28
  ratings_for_repetition = []
29
 
30
- ratings = CalificacionController.getRatingsByTechnique(
31
- technique=self.session.tecnica)
32
 
33
- if isinstance(ratings, dict) or not ratings:
34
  self.context["calificaciones"] = ratings_for_repetition
35
  self.context["existen_calificaciones"] = False
36
  return self.context
37
 
38
- data = DatoController.getWordValuesForConvecional(
39
- ratings=ratings, technique=self.session.tecnica)
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  ratings_for_repetition = defaultdict(
42
  lambda: defaultdict(lambda: defaultdict(list)))
43
 
44
  for item in data:
45
- user = item["usuarioCatador"]
46
  rep = item["repeticion"]
47
  prod = item["producto_code"]
48
 
@@ -54,3 +66,7 @@ class DetallesRATAController(DetallesController):
54
  self.context["calificaciones"] = defaultdict_to_dict(
55
  ratings_for_repetition)
56
  self.context["existen_calificaciones"] = True
 
 
 
 
 
1
+ from django.db.models import F
2
+ from tecnicas.models import SesionSensorial, Calificacion, ValorBooleano
3
+ from tecnicas.controllers import PalabrasController
 
4
  from tecnicas.utils import defaultdict_to_dict
5
  from .details_controller import DetallesController
6
  from collections import defaultdict
7
 
8
 
9
+ class DetallesCATAController(DetallesController):
10
  def __init__(self, session: SesionSensorial):
11
  super().__init__(session)
12
+ self.url_template = "tecnicas/manage_sesions/details-session-cata.html"
13
+ self.url_next = "cata_system:monitor_sesion"
 
 
 
 
 
14
 
15
  def getContext(self):
16
+ technique = self.session.tecnica
17
+
18
+ self.context = {
19
+ "sesion": self.session,
20
+ "use_technique": technique
21
+ }
22
+
23
+ # Recuperar palabras
24
  self.words = PalabrasController.getWordsInTechnique(
25
  self.session.tecnica)
26
  self.context["palabras"] = [word.nombre_palabra for word in self.words]
27
 
28
+ # Intentar recuperar las calificaciones
29
  ratings_for_repetition = []
30
 
31
+ ratings = list(Calificacion.objects.filter(
32
+ id_tecnica=technique))
33
 
34
+ if not ratings:
35
  self.context["calificaciones"] = ratings_for_repetition
36
  self.context["existen_calificaciones"] = False
37
  return self.context
38
 
39
+ data = (
40
+ ValorBooleano.objects
41
+ .filter(id_dato__id_calificacion__in=ratings)
42
+ .values(
43
+ nombre_palabra=F("id_dato__id_palabra__nombre_palabra"),
44
+ repeticion=F("id_dato__id_calificacion__num_repeticion"),
45
+ producto_code=F(
46
+ "id_dato__id_calificacion__id_producto__codigoProducto"),
47
+ usuario_catador=F(
48
+ "id_dato__id_calificacion__id_catador__user__username"),
49
+ dato_valor=F("valor")
50
+ )
51
+ )
52
 
53
  ratings_for_repetition = defaultdict(
54
  lambda: defaultdict(lambda: defaultdict(list)))
55
 
56
  for item in data:
57
+ user = item["usuario_catador"]
58
  rep = item["repeticion"]
59
  prod = item["producto_code"]
60
 
 
66
  self.context["calificaciones"] = defaultdict_to_dict(
67
  ratings_for_repetition)
68
  self.context["existen_calificaciones"] = True
69
+
70
+ # Se comprueba que ya no se pueda iniciar la repeticion
71
+ self.context["fin_repeticiones"] = technique.repeticion >= technique.repeticiones_max
72
+ return self.context
tecnicas/controllers/views_controller/session_management/details/details_controller.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, Presentador, Tecnica, Participacion
5
+ from tecnicas.controllers import ParticipacionController
6
+
7
+
8
+ class DetallesController():
9
+ url_template: str
10
+ url_next = "cata_system:monitor_sesion"
11
+
12
+ def __init__(self, session: SesionSensorial):
13
+ self.session = session
14
+
15
+ def controllGetResponse(self, request: HttpRequest, error: str = "", message: str = ""):
16
+ context = self.getContext()
17
+
18
+ if error != "" or error:
19
+ context["error"] = error
20
+ if message != "" or message:
21
+ context["message"] = message
22
+
23
+ return render(
24
+ request, self.url_template, context)
25
+
26
+ def getContext(self):
27
+ return {}
28
+
29
+ def deleteSesorialSession(self):
30
+ technique = Tecnica.objects.get(id=self.session.tecnica.id)
31
+ technique.delete()
32
+
33
+ def startRepetition(self, presenter: Presentador, request: HttpRequest):
34
+ creator = presenter
35
+ technique = self.session.tecnica
36
+
37
+ if creator.user.username != self.session.creadoPor.user.username:
38
+ return self.controllGetResponse(error="Solo el presentador que crea la sesión puede iniciar la repetición", request=request)
39
+ elif self.session.activo:
40
+ return self.controllGetResponse(error="La sesión ya está activada", request=request)
41
+ elif technique.repeticion >= technique.repeticiones_max:
42
+ return self.controllGetResponse(error="Se ha alcanzado el número de repeticiones máxima", request=request)
43
+
44
+ is_update_participations = self.setParticipationsToNoFinished()
45
+ if not is_update_participations:
46
+ return self.controllGetResponse(error="Error al actualizar las participaciones", request=request)
47
+
48
+ self.session.activo = True
49
+ technique.repeticion = technique.repeticion + 1
50
+
51
+ technique.save()
52
+ self.session.save()
53
+
54
+ parameters = {
55
+ "session_code": self.session.codigo_sesion
56
+ }
57
+ return redirect(
58
+ reverse(self.url_next, kwargs=parameters))
59
+
60
+ def setParticipationsToNoFinished(self):
61
+ there_participacions = Participacion.objects.filter(
62
+ tecnica=self.session.tecnica).exists()
63
+
64
+ if there_participacions:
65
+ (is_update_participations,
66
+ message) = ParticipacionController.outAllInSession(self.session)
67
+
68
+ return is_update_participations
69
+
70
+ return True
tecnicas/controllers/views_controller/session_management/{details_escala_controller.py → details/details_escala_controller.py} RENAMED
@@ -11,40 +11,26 @@ Encabezados de como deben de aparecer los datos juntos
11
  | Repeticion | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
12
 
13
  '''
14
- from django.http import HttpRequest
15
- from django.shortcuts import render, redirect
16
- from django.urls import reverse
17
- from tecnicas.models import SesionSensorial, Presentador, Participacion, Calificacion, Escala
18
- from tecnicas.controllers import DatoController, PalabrasController, ParticipacionController
19
  from .details_controller import DetallesController
20
- from tecnicas.utils import defaultdict_to_dict, controller_error
21
  from collections import defaultdict
22
 
23
 
24
  class DetallesEscalasController(DetallesController):
25
- url_template = "tecnicas/manage_sesions/detalles-sesion.html"
26
-
27
  def __init__(self, session: SesionSensorial):
28
  super().__init__(session)
29
-
30
- def getResponse(self, request: HttpRequest, error: str = "", message: str = ""):
31
- context = self.getContext()
32
-
33
- if error != "" or error:
34
- context["error"] = error
35
- if message != "" or message:
36
- context["message"] = message
37
-
38
- return render(
39
- request, self.url_template, context)
40
 
41
  def getContext(self):
 
 
42
  self.context = {
43
- "use_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
 
44
  }
45
- self.context["sesion"] = self.session
46
-
47
- technique = self.session.tecnica
48
 
49
  # Datos de la escala usada
50
  scale: Escala = technique.escala_tecnica
@@ -94,35 +80,3 @@ class DetallesEscalasController(DetallesController):
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
120
-
121
- technique.save()
122
- self.session.save()
123
-
124
- parameters = {
125
- "session_code": self.session.codigo_sesion
126
- }
127
- return redirect(
128
- reverse("cata_system:monitor_sesion", kwargs=parameters))
 
11
  | Repeticion | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
12
 
13
  '''
14
+ from tecnicas.models import SesionSensorial, Calificacion, Escala
15
+ from tecnicas.controllers import DatoController, PalabrasController
 
 
 
16
  from .details_controller import DetallesController
17
+ from tecnicas.utils import defaultdict_to_dict
18
  from collections import defaultdict
19
 
20
 
21
  class DetallesEscalasController(DetallesController):
 
 
22
  def __init__(self, session: SesionSensorial):
23
  super().__init__(session)
24
+ self.url_template = "tecnicas/manage_sesions/details-session.html"
25
+ self.url_next = "cata_system:monitor_sesion"
 
 
 
 
 
 
 
 
 
26
 
27
  def getContext(self):
28
+ technique = self.session.tecnica
29
+
30
  self.context = {
31
+ "sesion": self.session,
32
+ "use_technique": technique.tipo_tecnica.nombre_tecnica
33
  }
 
 
 
34
 
35
  # Datos de la escala usada
36
  scale: Escala = technique.escala_tecnica
 
80
  self.context["fin_repeticiones"] = technique.repeticion >= technique.repeticiones_max
81
 
82
  return self.context
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/controllers/views_controller/session_management/details/details_napping_controller.py ADDED
@@ -0,0 +1,508 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect
3
+ from django.urls import reverse
4
+ from django.db.models import F
5
+ from .details_controller import DetallesController
6
+ from tecnicas.models import (
7
+ SesionSensorial, Presentador, Modalidad, TecnicaModalidad, Catador,
8
+ Participacion, DatoPunto, Calificacion, GrupoProducto, ValorBooleano,
9
+ ValorDecimal, Producto, Escala, EsVocabulario, Vocabulario
10
+ )
11
+ from tecnicas.utils import defaultdict_to_dict
12
+ from collections import defaultdict
13
+
14
+
15
+ class DetallesNappingController(DetallesController):
16
+ def __init__(self, session: SesionSensorial):
17
+ super().__init__(session)
18
+ self.url_template = "tecnicas/manage_sesions/details-session-napping.html"
19
+ self.url_next = "cata_system:monitor_sesion"
20
+ self.context = {}
21
+
22
+ def getContext(self):
23
+ self.context["session"] = self.session
24
+
25
+ self.defineStatus()
26
+ self.setIsEndSession()
27
+ self.setDataTable()
28
+
29
+ return self.context
30
+
31
+ def defineStatus(self):
32
+ repetition = self.session.tecnica.repeticion
33
+ mod = TecnicaModalidad.objects.get(
34
+ tecnica=self.session.tecnica)
35
+
36
+ self.context["mod_tech"] = mod.modalidad.nombre
37
+ self.context["mode"] = mod.modalidad.nombre
38
+ if mod.modalidad.nombre == "sin modalidad":
39
+ self.context["mod_tech"] = "No se usa modalidad"
40
+
41
+ if not self.session.activo:
42
+ self.context["status"] = "Listo para iniciar la sesión con Napping"
43
+ elif self.session.activo:
44
+ self.context["status"] = "Sesión con en curso"
45
+
46
+ def controllPostResponse(self, request: HttpRequest, action: str):
47
+ if action == "start_sin_modalidad":
48
+ response = self.startNapping(request=request)
49
+
50
+ elif action == "start_perfil_ultra_flash":
51
+ response = self.startNapping(request=request)
52
+
53
+ elif action == "start_sorting":
54
+ response = self.startNapping(request=request)
55
+
56
+ elif action == "combine_sessions":
57
+ response = self.combineSessions(request=request)
58
+
59
+ elif action == "delete_session":
60
+ self.deleteSesorialSession()
61
+ response = redirect(
62
+ reverse("cata_system:panel_sesiones", kwargs={"page": 1}))
63
+
64
+ else:
65
+ response = self.controllGetResponse(
66
+ error="Modalidad sin implantar", request=request)
67
+
68
+ return response
69
+
70
+ def startNapping(self, request: HttpRequest):
71
+ if request.user.user_presentador.user.username != self.session.creadoPor.user.username:
72
+ return self.controllGetResponse(error="Solo el presentador que crea la sesión puede iniciar la repetición", request=request)
73
+ elif self.session.activo:
74
+ return self.controllGetResponse(error="La sesión ya está activada", request=request)
75
+
76
+ is_update_participations = self.setParticipationsToNoFinished()
77
+ if not is_update_participations:
78
+ return self.controllGetResponse(error="Error al actualizar las participaciones", request=request)
79
+
80
+ self.session.activo = True
81
+ self.session.save()
82
+
83
+ parameters = {
84
+ "session_code": self.session.codigo_sesion
85
+ }
86
+ return redirect(
87
+ reverse(self.url_next, kwargs=parameters))
88
+
89
+ def setDataTable(self):
90
+ participations = Participacion.objects.filter(
91
+ tecnica=self.session.tecnica).select_related("catador")
92
+ testers = [participation.catador for participation in participations]
93
+ self.context["testers"] = testers
94
+
95
+ ratings = Calificacion.objects.filter(id_tecnica=self.session.tecnica)
96
+
97
+ coordinates = (
98
+ DatoPunto.objects.filter(calificacion__in=ratings)
99
+ .values(
100
+ producto=F("calificacion__id_producto__codigoProducto"),
101
+ catador=F("calificacion__id_catador__user__username"),
102
+ px=F("x"),
103
+ py=F("y"),
104
+ ))
105
+
106
+ if not coordinates.exists():
107
+ self.context["there_data"] = False
108
+ return []
109
+
110
+ coordinates_by_product = defaultdict(dict)
111
+
112
+ for coordinate in coordinates:
113
+ coordinates_by_product[coordinate["producto"]][coordinate["catador"]] = {
114
+ "px": coordinate["px"],
115
+ "py": coordinate["py"],
116
+ }
117
+
118
+ self.context["coordinates_no_mode"] = defaultdict_to_dict(
119
+ coordinates_by_product)
120
+
121
+ # Add word frequency data for perfil ultra flash mode
122
+ mod = TecnicaModalidad.objects.get(tecnica=self.session.tecnica)
123
+ if mod.modalidad.nombre == "perfil ultra flash":
124
+ self.setWordFrequencies(ratings)
125
+ elif mod.modalidad.nombre == "sorting":
126
+ self.setSortingData()
127
+
128
+ self.context["there_data"] = True
129
+
130
+ def setWordFrequencies(self, ratings):
131
+ from collections import Counter
132
+
133
+ # Prefetch palabras to optimize queries
134
+ ratings_with_words = ratings.prefetch_related(
135
+ 'palabras').select_related('id_producto')
136
+
137
+ # Dictionary to store word frequencies by product
138
+ word_frequencies_by_product = defaultdict(Counter)
139
+ all_words_set = set()
140
+
141
+ for rating in ratings_with_words:
142
+ producto_code = rating.id_producto.codigoProducto
143
+ words = rating.palabras.all()
144
+
145
+ for word in words:
146
+ word_name = word.nombre_palabra
147
+ word_frequencies_by_product[producto_code][word_name] += 1
148
+ all_words_set.add(word_name)
149
+
150
+ # Convert Counter objects to regular dicts and sort words alphabetically
151
+ word_frequencies_dict = {
152
+ product: dict(frequencies)
153
+ for product, frequencies in word_frequencies_by_product.items()
154
+ }
155
+
156
+ # Sort all words alphabetically for consistent column ordering
157
+ all_words_sorted = sorted(all_words_set)
158
+
159
+ self.context["word_frequencies"] = word_frequencies_dict
160
+ self.context["all_words"] = all_words_sorted
161
+
162
+ def setSortingData(self):
163
+ # Get all ratings for this technique to access DatoPunto
164
+ ratings = Calificacion.objects.filter(id_tecnica=self.session.tecnica)
165
+
166
+ # Get coordinates for all products
167
+ coordinates = (
168
+ DatoPunto.objects.filter(calificacion__in=ratings)
169
+ .values(
170
+ producto=F("calificacion__id_producto__codigoProducto"),
171
+ producto_id=F("calificacion__id_producto__id"),
172
+ catador=F("calificacion__id_catador__user__username"),
173
+ catador_id=F("calificacion__id_catador__id"),
174
+ px=F("x"),
175
+ py=F("y"),
176
+ ))
177
+
178
+ # Create a mapping of (catador_id, producto_id) -> coordinates
179
+ coord_map = {}
180
+ for coord in coordinates:
181
+ key = (coord["catador_id"], coord["producto_id"])
182
+ coord_map[key] = {
183
+ "px": coord["px"],
184
+ "py": coord["py"],
185
+ "producto": coord["producto"],
186
+ "catador": coord["catador"]
187
+ }
188
+
189
+ # Get all groups with their products and words
190
+ grupos = (
191
+ GrupoProducto.objects.filter(tecnica=self.session.tecnica)
192
+ .prefetch_related("productos", "palabras")
193
+ .select_related("catador__user")
194
+ )
195
+
196
+ # Create a mapping of (catador_id, producto_id) -> words
197
+ words_map = defaultdict(list)
198
+ for grupo in grupos:
199
+ catador_id = grupo.catador.id
200
+ words = [palabra.nombre_palabra for palabra in grupo.palabras.all()]
201
+ words_str = ";".join(words) if words else ""
202
+
203
+ for producto in grupo.productos.all():
204
+ key = (catador_id, producto.id)
205
+ words_map[key] = words_str
206
+
207
+ # Structure final data: product -> catador -> {px, py, words}
208
+ sorting_data = defaultdict(dict)
209
+
210
+ for key, coord_data in coord_map.items():
211
+ catador_id, producto_id = key
212
+ producto_code = coord_data["producto"]
213
+ catador_username = coord_data["catador"]
214
+
215
+ sorting_data[producto_code][catador_username] = {
216
+ "px": coord_data["px"],
217
+ "py": coord_data["py"],
218
+ "words": words_map.get(key, "")
219
+ }
220
+
221
+ self.context["sorting_data"] = defaultdict_to_dict(sorting_data)
222
+
223
+ def setIsEndSession(self):
224
+ if not self.session.activo and self.session.tecnica.repeticion < 1:
225
+ self.context["finished"] = False
226
+ return
227
+ elif self.session.activo:
228
+ self.context["finished"] = False
229
+ return
230
+ elif not self.session.activo and self.session.tecnica.repeticion >= 1:
231
+ self.context["finished"] = True
232
+ return
233
+
234
+ # ==================== SESSION COMBINATION METHODS ====================
235
+
236
+ def combineSessions(self, request: HttpRequest):
237
+ """Handle session combination request"""
238
+ session_b_code = request.POST.get("session_b_code", "").strip()
239
+
240
+ if not session_b_code:
241
+ return self.controllGetResponse(
242
+ error="Debe proporcionar un código de sesión", request=request)
243
+
244
+ # Validate and get session B
245
+ validation_result = self.validateSessionCombination(session_b_code)
246
+
247
+ if validation_result.get("error"):
248
+ return self.controllGetResponse(
249
+ error=validation_result["error"], request=request)
250
+
251
+ session_b = validation_result["session_b"]
252
+ technique_type = validation_result["technique_type"]
253
+
254
+ # Get combined data based on technique type
255
+ if technique_type == "cata":
256
+ combined_data = self.getCombinedDataForCATA(session_b)
257
+ elif technique_type == "rata":
258
+ combined_data = self.getCombinedDataForRATA(session_b)
259
+ elif technique_type == "escalas":
260
+ combined_data = self.getCombinedDataForEscalas(session_b)
261
+ else:
262
+ return self.controllGetResponse(
263
+ error="Tipo de técnica no soportado para combinación", request=request)
264
+
265
+ # Add combined data to context
266
+ self.context["combined_data"] = combined_data
267
+ self.context["session_b"] = session_b
268
+ self.context["session_b_technique_type"] = technique_type
269
+
270
+ return self.controllGetResponse(request=request)
271
+
272
+ def validateSessionCombination(self, session_b_code: str):
273
+ """Validate that Session B can be combined with Session A (Napping)"""
274
+ result = {"error": None, "session_b": None, "technique_type": None}
275
+
276
+ # Check if Session B exists
277
+ try:
278
+ session_b = SesionSensorial.objects.select_related(
279
+ "tecnica__tipo_tecnica").get(codigo_sesion=session_b_code)
280
+ except SesionSensorial.DoesNotExist:
281
+ result["error"] = f"No existe una sesión con el código: {session_b_code}"
282
+ return result
283
+
284
+ # Check if Session B technique is CATA, RATA, or Escalas
285
+ technique_type = session_b.tecnica.tipo_tecnica.nombre_tecnica
286
+ valid_techniques = ["cata", "rata", "escalas"]
287
+
288
+ if technique_type not in valid_techniques:
289
+ result[
290
+ "error"] = f"La sesión B debe usar CATA, RATA o Escalas. Técnica actual: {technique_type}"
291
+ return result
292
+
293
+ # Check if Session B is finished
294
+ if session_b.activo:
295
+ result["error"] = "La sesión B debe estar finalizada (no activa)"
296
+ return result
297
+
298
+ if session_b.tecnica.repeticion < 1:
299
+ result["error"] = "La sesión B debe haber completado al menos una repetición"
300
+ return result
301
+
302
+ # Get products from both sessions
303
+ products_a = set(
304
+ Producto.objects.filter(
305
+ calificacion_producto__id_tecnica=self.session.tecnica
306
+ ).values_list("codigoProducto", flat=True).distinct()
307
+ )
308
+
309
+ products_b = set(
310
+ Producto.objects.filter(
311
+ calificacion_producto__id_tecnica=session_b.tecnica
312
+ ).values_list("codigoProducto", flat=True).distinct()
313
+ )
314
+
315
+ # Check if products match
316
+ if products_a != products_b:
317
+ result["error"] = f"Los productos no coinciden. Sesión A: {len(products_a)} productos, Sesión B: {len(products_b)} productos"
318
+ return result
319
+
320
+ # Get tasters from both sessions
321
+ tasters_a = set(
322
+ Participacion.objects.filter(
323
+ tecnica=self.session.tecnica
324
+ ).values_list("catador__user__username", flat=True)
325
+ )
326
+
327
+ tasters_b = set(
328
+ Participacion.objects.filter(
329
+ tecnica=session_b.tecnica
330
+ ).values_list("catador__user__username", flat=True)
331
+ )
332
+
333
+ # Check if tasters match
334
+ if tasters_a != tasters_b:
335
+ result["error"] = f"Los catadores no coinciden. Sesión A: {len(tasters_a)} catadores, Sesión B: {len(tasters_b)} catadores"
336
+ return result
337
+
338
+ result["session_b"] = session_b
339
+ result["technique_type"] = technique_type
340
+ return result
341
+
342
+ def getCombinedDataForCATA(self, session_b: SesionSensorial):
343
+ """Get combined data for CATA technique (word frequencies)"""
344
+ from collections import Counter
345
+
346
+ # Get all ratings for session B
347
+ ratings_b = Calificacion.objects.filter(id_tecnica=session_b.tecnica)
348
+
349
+ # Get boolean values (CATA uses boolean)
350
+ data = (
351
+ ValorBooleano.objects
352
+ .filter(id_dato__id_calificacion__in=ratings_b, valor=True)
353
+ .values(
354
+ palabra=F("id_dato__id_palabra__nombre_palabra"),
355
+ producto=F(
356
+ "id_dato__id_calificacion__id_producto__codigoProducto"),
357
+ )
358
+ )
359
+
360
+ # Count word frequencies per product
361
+ word_frequencies = defaultdict(Counter)
362
+ all_words_set = set()
363
+
364
+ for item in data:
365
+ palabra = item["palabra"]
366
+ producto = item["producto"]
367
+ word_frequencies[producto][palabra] += 1
368
+ all_words_set.add(palabra)
369
+
370
+ # Get vocabulary info if exists
371
+ vocabulary_info = self.getVocabularyInfo(session_b.tecnica)
372
+
373
+ return {
374
+ "word_frequencies": defaultdict_to_dict(word_frequencies),
375
+ "all_words": sorted(all_words_set),
376
+ "vocabulary_info": vocabulary_info,
377
+ }
378
+
379
+ def getCombinedDataForRATA(self, session_b: SesionSensorial):
380
+ """Get combined data for RATA technique (word averages)"""
381
+
382
+ # Get all ratings for session B
383
+ ratings_b = Calificacion.objects.filter(id_tecnica=session_b.tecnica)
384
+
385
+ # Get decimal values (RATA uses decimal)
386
+ data = (
387
+ ValorDecimal.objects
388
+ .filter(id_dato__id_calificacion__in=ratings_b)
389
+ .values(
390
+ palabra=F("id_dato__id_palabra__nombre_palabra"),
391
+ producto=F(
392
+ "id_dato__id_calificacion__id_producto__codigoProducto"),
393
+ valor_decimal=F("valor"),
394
+ )
395
+ )
396
+
397
+ # Calculate averages per product per word
398
+ word_sums = defaultdict(lambda: defaultdict(list))
399
+ all_words_set = set()
400
+
401
+ for item in data:
402
+ palabra = item["palabra"]
403
+ producto = item["producto"]
404
+ valor = item["valor_decimal"]
405
+ word_sums[producto][palabra].append(valor)
406
+ all_words_set.add(palabra)
407
+
408
+ # Calculate averages
409
+ word_averages = {}
410
+ for producto, palabras in word_sums.items():
411
+ word_averages[producto] = {}
412
+ for palabra, valores in palabras.items():
413
+ word_averages[producto][palabra] = sum(valores) / len(valores)
414
+
415
+ # Get vocabulary info if exists
416
+ vocabulary_info = self.getVocabularyInfo(session_b.tecnica)
417
+
418
+ return {
419
+ "word_averages": word_averages,
420
+ "all_words": sorted(all_words_set),
421
+ "vocabulary_info": vocabulary_info,
422
+ }
423
+
424
+ def getCombinedDataForEscalas(self, session_b: SesionSensorial):
425
+ """Get combined data for Escalas technique (averages across repetitions)"""
426
+
427
+ # Get all ratings for session B
428
+ ratings_b = Calificacion.objects.filter(id_tecnica=session_b.tecnica)
429
+
430
+ # Get decimal values grouped by repetition
431
+ data = (
432
+ ValorDecimal.objects
433
+ .filter(id_dato__id_calificacion__in=ratings_b)
434
+ .values(
435
+ palabra=F("id_dato__id_palabra__nombre_palabra"),
436
+ producto=F(
437
+ "id_dato__id_calificacion__id_producto__codigoProducto"),
438
+ repeticion=F("id_dato__id_calificacion__num_repeticion"),
439
+ valor_decimal=F("valor"),
440
+ )
441
+ )
442
+
443
+ # Calculate averages per repetition (like RATA), then average across repetitions
444
+ # Structure: {producto: {repeticion: {palabra: [valores]}}}
445
+ repetition_values = defaultdict(
446
+ lambda: defaultdict(lambda: defaultdict(list)))
447
+ all_words_set = set()
448
+
449
+ for item in data:
450
+ palabra = item["palabra"]
451
+ producto = item["producto"]
452
+ repeticion = item["repeticion"]
453
+ valor = item["valor_decimal"]
454
+ # Collect all values for averaging
455
+ repetition_values[producto][repeticion][palabra].append(valor)
456
+ all_words_set.add(palabra)
457
+
458
+ # Calculate average per repetition, then average across repetitions
459
+ word_averages = {}
460
+ for producto, repeticiones in repetition_values.items():
461
+ word_averages[producto] = {}
462
+ # Get all words for this product across all repetitions
463
+ all_product_words = set()
464
+ for rep_words in repeticiones.values():
465
+ all_product_words.update(rep_words.keys())
466
+
467
+ # Calculate average for each word
468
+ for palabra in all_product_words:
469
+ # Get average for each repetition
470
+ rep_averages = []
471
+ for rep in repeticiones.keys():
472
+ if palabra in repeticiones[rep]:
473
+ valores = repeticiones[rep][palabra]
474
+ rep_averages.append(sum(valores) / len(valores))
475
+
476
+ # Average the repetition averages
477
+ if rep_averages:
478
+ word_averages[producto][palabra] = sum(
479
+ rep_averages) / len(rep_averages)
480
+
481
+ # Get scale and vocabulary info
482
+ scale = Escala.objects.get(tecnica=session_b.tecnica)
483
+ scale_info = None
484
+ if scale:
485
+ scale_info = {
486
+ "type": scale.id_tipo_escala.nombre_escala,
487
+ "size": scale.longitud
488
+ }
489
+
490
+ vocabulary_info = self.getVocabularyInfo(session_b.tecnica)
491
+
492
+ return {
493
+ "word_averages": word_averages,
494
+ "all_words": sorted(all_words_set),
495
+ "scale_info": scale_info,
496
+ "vocabulary_info": vocabulary_info,
497
+ "num_repetitions": session_b.tecnica.repeticion,
498
+ }
499
+
500
+ def getVocabularyInfo(self, tecnica):
501
+ es_vocabulario = EsVocabulario.objects.filter(
502
+ id_tecnica=tecnica).first()
503
+ if es_vocabulario:
504
+ vocabulario = es_vocabulario.id_vocabulario
505
+ return {
506
+ "nombre": vocabulario.nombre_vocabulario,
507
+ }
508
+ return None
tecnicas/controllers/views_controller/session_management/details/details_pf_controller.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import SesionSensorial, ListaPalabras, Calificacion, Catador
2
+ from tecnicas.controllers import DatoController
3
+ from tecnicas.utils import defaultdict_to_dict
4
+ from .details_controller import DetallesController
5
+ from collections import defaultdict
6
+
7
+
8
+ class DetallesPFController(DetallesController):
9
+ skip_repetition = 2
10
+
11
+ def __init__(self, session: SesionSensorial):
12
+ super().__init__(session)
13
+ self.url_template = "tecnicas/manage_sesions/details-session-pf.html"
14
+ self.url_next = "cata_system:monitor_sesion"
15
+
16
+ def getContext(self):
17
+ technique = self.session.tecnica
18
+
19
+ self.context = {
20
+ "sesion": self.session,
21
+ "use_technique": technique,
22
+ "tipo_escala": technique.escala_tecnica.id_tipo_escala.nombre_escala,
23
+ "repeticiones_max": technique.repeticiones_max - self.skip_repetition
24
+ }
25
+
26
+ # Definir el estado de la sesion
27
+ rep = technique.repeticion
28
+ activate = self.session.activo
29
+ self.context["estado"] = self.getStatus(rep, activate)
30
+
31
+ self.getDataPhases()
32
+
33
+ self.isEndSession()
34
+
35
+ return self.context
36
+
37
+ def isEndSession(self):
38
+ current_rep = self.session.tecnica.repeticion - self.skip_repetition
39
+ max_rep = self.session.tecnica.repeticiones_max - self.skip_repetition
40
+ self.context["finished"] = current_rep >= max_rep
41
+
42
+ def getDataPhases(self):
43
+ curren_repetition = self.session.tecnica.repeticion
44
+
45
+ if curren_repetition == 1:
46
+ self.context["fisrt_phase"] = self.getDataFirstPhase()
47
+ self.context["repeticion"] = 0
48
+
49
+ elif curren_repetition == 2:
50
+ self.context["fisrt_phase"] = self.getDataFirstPhase()
51
+ self.context["second_phase"] = self.getDataSecondPhase()
52
+ self.context["repeticion"] = 0
53
+
54
+ elif curren_repetition >= 3:
55
+ self.context["fisrt_phase"] = self.getDataFirstPhase()
56
+ self.context["second_phase"] = self.getDataSecondPhase()
57
+ self.context["data_ratings"] = self.getDataRatings()
58
+ self.context["repeticion"] = self.session.tecnica.repeticion - self.skip_repetition
59
+
60
+ return self.context
61
+
62
+ def getDataFirstPhase(self):
63
+ lists_testers = ListaPalabras.objects.filter(
64
+ tecnica=self.session.tecnica,
65
+ es_final=False
66
+ )
67
+
68
+ result = []
69
+ for list in lists_testers:
70
+ try:
71
+ username = list.catador.user.username
72
+ except Exception:
73
+ username = None
74
+
75
+ words_qs = list.palabras.all()
76
+ words = []
77
+ for p in words_qs:
78
+ nombre = getattr(p, 'nombre_palabra', None)
79
+ words.append({
80
+ 'id': getattr(p, 'id', None),
81
+ 'nombre_palabra': nombre
82
+ })
83
+
84
+ result.append({
85
+ 'username': username,
86
+ 'words': words
87
+ })
88
+
89
+ return result
90
+
91
+ def getDataSecondPhase(self):
92
+ lists_testers = ListaPalabras.objects.filter(
93
+ tecnica=self.session.tecnica,
94
+ es_final=True
95
+ )
96
+
97
+ result = []
98
+ for list in lists_testers:
99
+ try:
100
+ username = list.catador.user.username
101
+ except Exception:
102
+ username = None
103
+
104
+ words_qs = list.palabras.all()
105
+ words = []
106
+ for p in words_qs:
107
+ nombre = getattr(p, 'nombre_palabra', None)
108
+ words.append({
109
+ 'id': getattr(p, 'id', None),
110
+ 'nombre_palabra': nombre
111
+ })
112
+
113
+ result.append({
114
+ 'username': username,
115
+ 'words': words
116
+ })
117
+
118
+ return result
119
+
120
+ def getDataRatings(self):
121
+ technique = self.session.tecnica
122
+
123
+ if technique.repeticion > 3:
124
+ return self.getDataRatingsFinal()
125
+
126
+ elif technique.repeticion == 3:
127
+ return self.getDataRatingsInitial()
128
+
129
+ def getStatus(self, rep: int, activate: bool):
130
+ status = ""
131
+
132
+ if rep == 0 and not activate:
133
+ status = "Listo para crear listas iniciales"
134
+
135
+ elif rep == 1 and activate:
136
+ status = "En primera fase, creación de listas iniciales"
137
+ elif rep == 1 and not activate:
138
+ status = "Listo para crear listas finales"
139
+
140
+ elif rep == 2 and activate:
141
+ status = "En segunda fase, creación de listas finales"
142
+ elif rep == 2 and not activate:
143
+ status = "Listo para calificaciones"
144
+
145
+ elif rep > 2 and not activate:
146
+ status = "Listo para calificaciones"
147
+ elif rep > 2 and activate:
148
+ status = "Catadores calificando"
149
+
150
+ return status
151
+
152
+ def getDataRatingsInitial(self):
153
+ ratings = list(Calificacion.objects.filter(id_tecnica=self.session.tecnica, num_repeticion=3))
154
+
155
+ if ratings:
156
+ raw_data = DatoController.getWordValuesForConvecional(
157
+ technique=self.session.tecnica,
158
+ ratings=ratings
159
+ )
160
+
161
+ structured_data = defaultdict(lambda: defaultdict(list))
162
+
163
+ for item in raw_data:
164
+ prod_code = item["producto_code"]
165
+ username = item["usuario_catador"]
166
+
167
+ structured_data[prod_code][username].append({
168
+ "palabra": item["nombre_palabra"],
169
+ "valor": item["dato_valor"]
170
+ })
171
+
172
+ return defaultdict_to_dict(structured_data)
173
+
174
+ def getDataRatingsFinal(self):
175
+ lists_words_testers = self.context["second_phase"]
176
+ technique = self.session.tecnica
177
+
178
+ ratings_for_tester = []
179
+
180
+ for list_tester in lists_words_testers:
181
+ tester_username = list_tester["username"]
182
+ # Se recuperan las calificaciones
183
+ ratings_for_repetition = []
184
+
185
+ ratings = list(Calificacion.objects.filter(
186
+ id_tecnica=technique, id_catador__user__username=tester_username))
187
+
188
+ if not ratings:
189
+ continue
190
+
191
+ data = DatoController.getWordValuesPF(
192
+ ratings=ratings, technique=technique, tester=Catador.objects.get(user__username=tester_username))
193
+
194
+ ratings_for_repetition = defaultdict(lambda: defaultdict(list))
195
+
196
+ # Estructurar los datos
197
+ for item in data:
198
+ rep = item["repeticion"]
199
+ prod = item["producto_code"]
200
+
201
+ ratings_for_repetition[rep-2][prod].append({
202
+ "nombre_palabra": item["nombre_palabra"],
203
+ "dato_valor": item["dato_valor"]
204
+ })
205
+
206
+ ratings_for_tester.append(
207
+ {
208
+ "tester": tester_username,
209
+ "ratings": defaultdict_to_dict(
210
+ ratings_for_repetition),
211
+ "words": list_tester["words"]
212
+ }
213
+ )
214
+
215
+ return ratings_for_tester
tecnicas/controllers/views_controller/session_management/details/details_rata_controller.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import SesionSensorial
2
+ from .details_controller import DetallesController
3
+
4
+
5
+ class DetallesRATAController(DetallesController):
6
+ def __init__(self, session: SesionSensorial):
7
+ super().__init__(session)
tecnicas/controllers/views_controller/session_management/details/details_sort_controller.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .details_controller import DetallesController
2
+ from tecnicas.models import SesionSensorial, GrupoProducto, Producto, Participacion
3
+ from collections import defaultdict
4
+
5
+
6
+ class DetallesSortController(DetallesController):
7
+ def __init__(self, session: SesionSensorial):
8
+ super().__init__(session)
9
+ self.url_template = "tecnicas/manage_sesions/details-session-sort.html"
10
+ self.url_next = "cata_system:monitor_sesion"
11
+
12
+ def getContext(self):
13
+ technique = self.session.tecnica
14
+
15
+ finished = False
16
+ status = ""
17
+
18
+ if technique.repeticion < technique.repeticiones_max and not self.session.activo:
19
+ status = "En espera para iniciar la sesión"
20
+ elif technique.repeticion >= technique.repeticiones_max and not self.session.activo:
21
+ status = "Esta sesión ha sido finalizada"
22
+ finished = True
23
+ else:
24
+ status = "La sesión está en progreso"
25
+
26
+ self.context = {
27
+ "sesion": self.session,
28
+ "technique": technique,
29
+ "status": status,
30
+ "finished": finished
31
+ }
32
+
33
+ self.context["data_groups"] = {
34
+ "data": self.setDataSort(),
35
+ "testers": self.setHeaders()
36
+ }
37
+
38
+ return self.context
39
+
40
+ def setDataSort(self):
41
+ data = []
42
+ technique = self.session.tecnica
43
+
44
+ products = Producto.objects.filter(id_tecnica=technique)
45
+
46
+ groups = GrupoProducto.objects.select_related("catador").filter(
47
+ tecnica=technique
48
+ )
49
+
50
+ if len(groups):
51
+ self.context["there_data"] = True
52
+ else:
53
+ self.context["there_data"] = False
54
+ return []
55
+
56
+ for product in products:
57
+ product_data = {
58
+ "codigo_producto": product.codigoProducto,
59
+ "palabras": {}
60
+ }
61
+
62
+ related_groups = groups.filter(productos=product).select_related(
63
+ "catador__user"
64
+ ).prefetch_related("palabras")
65
+
66
+ data_words = defaultdict(set)
67
+
68
+ for group in related_groups:
69
+ catador_username = group.catador.user.username
70
+
71
+ for word in group.palabras.all():
72
+ data_words[catador_username].add(word.nombre_palabra)
73
+
74
+ product_data["palabras"] = {
75
+ username: list(words)
76
+ for username, words in data_words.items()
77
+ }
78
+
79
+ data.append(product_data)
80
+
81
+ return data
82
+
83
+ def setHeaders(self):
84
+ participacions = list(Participacion.objects.filter(
85
+ tecnica=self.session.tecnica
86
+ ).only("catador").select_related("catador__user"))
87
+
88
+ testers = [
89
+ participacion.catador.user.username for participacion in participacions]
90
+
91
+ return testers
tecnicas/controllers/views_controller/session_management/details_controller.py DELETED
@@ -1,13 +0,0 @@
1
- from tecnicas.models import SesionSensorial, Presentador, Tecnica
2
- from tecnicas.utils import controller_error
3
-
4
-
5
- class DetallesController():
6
- url_template = "tecnicas/manage_sesions/detalles-sesion.html"
7
-
8
- def __init__(self, session: SesionSensorial):
9
- self.session = session
10
-
11
- def deleteSesorialSession(self):
12
- technique = Tecnica.objects.get(id=self.session.tecnica.id)
13
- technique.delete()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/controllers/views_controller/session_management/{monitor_controller.py → monitor/monitor_controller.py} RENAMED
@@ -1,8 +1,7 @@
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():
@@ -12,9 +11,21 @@ class MonitorController():
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,
@@ -26,7 +37,7 @@ class MonitorController():
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:
@@ -36,7 +47,7 @@ class MonitorController():
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
@@ -52,8 +63,6 @@ class MonitorController():
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
 
1
  from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import SesionSensorial, Producto, EsAtributo, EsVocabulario, Participacion
 
5
 
6
 
7
  class MonitorController():
 
11
  def __init__(self, session: SesionSensorial):
12
  self.sensorial_session = session
13
 
14
+ def controllPostFinishSession(self, request: HttpRequest):
15
+ (is_all_end, message) = self.checkAllFinish()
16
+ if not is_all_end:
17
+ self.setContext()
18
+ self.context["error"] = message
19
+ return render(request, self.url_view, self.context)
20
+ self.finishSession()
21
+ return redirect(reverse(self.previus_view, kwargs={"session_code": self.sensorial_session.codigo_sesion}))
22
+
23
+ def checkAllFinish(self) -> (bool, str):
24
+ return (False, "Función sin implementar")
25
+
26
  def setContext(self):
27
+ self.participations = Participacion.objects.filter(
28
+ tecnica=self.sensorial_session.tecnica)
29
 
30
  self.context = {
31
  "code_session": self.sensorial_session.codigo_sesion,
 
37
  "use_technique": self.sensorial_session.tecnica.tipo_tecnica.nombre_tecnica
38
  }
39
 
40
+ def controllGetResponse(self, request: HttpRequest, error: str = "", message: str = ""):
41
  self.setContext()
42
 
43
  if error != "" or error:
 
47
 
48
  return render(request, self.url_view, self.context)
49
 
50
+ def getExpectedRatings(self):
51
  num_products = Producto.objects.filter(
52
  id_tecnica=self.sensorial_session.tecnica).count()
53
  style_words = self.sensorial_session.tecnica.id_estilo
 
63
  return num_products * num_words
64
 
65
  def finishSession(self):
66
+ self.sensorial_session.activo = False
67
+ self.sensorial_session.save()
 
 
68
  return self.sensorial_session
tecnicas/controllers/views_controller/session_management/{monitor_escalas_controller.py → monitor/monitor_escalas_controller.py} RENAMED
@@ -1,6 +1,3 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import render, redirect
3
- from django.urls import reverse
4
  from tecnicas.models import Dato, Participacion
5
  from tecnicas.controllers import SesionController
6
  from .monitor_controller import MonitorController
@@ -12,23 +9,10 @@ class MonitorEscalasController(MonitorController):
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))
 
 
 
 
1
  from tecnicas.models import Dato, Participacion
2
  from tecnicas.controllers import SesionController
3
  from .monitor_controller import MonitorController
 
9
  self.url_view = "tecnicas/manage_sesions/monitor-sesion.html"
10
  self.previus_view = "cata_system:detalles_sesion"
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  def checkAllFinish(self):
13
  technique = self.sensorial_session.tecnica
14
 
15
+ expected_ratings_repetition = self.getExpectedRatings()
16
 
17
  all_participations = list(
18
  Participacion.objects.filter(tecnica=technique))
tecnicas/controllers/views_controller/session_management/monitor/monitor_napping_controller.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import SesionSensorial
2
+ from tecnicas.models import Participacion, TecnicaModalidad
3
+ from .monitor_controller import MonitorController
4
+
5
+
6
+ class MonitorNappingController(MonitorController):
7
+ def __init__(self, session: SesionSensorial):
8
+ super().__init__(session)
9
+ self.url_view = "tecnicas/manage_sesions/monitor-session-sort.html"
10
+ self.previus_view = "cata_system:detalles_sesion"
11
+
12
+ def checkAllFinish(self) -> (bool, str):
13
+ technique = self.sensorial_session.tecnica
14
+
15
+ num_participations = Participacion.objects.filter(
16
+ tecnica=technique).count()
17
+
18
+ if num_participations < technique.limite_catadores:
19
+ return (False, "No se ha alcanzado el número máximo de catadores")
20
+
21
+ unfinished_participations = Participacion.objects.filter(
22
+ tecnica=technique, finalizado=False).count()
23
+
24
+ if unfinished_participations > 0:
25
+ return (False, "No todos los catadores han finalizado su evaluación")
26
+
27
+ return (True, "Puedes finalizar la sesión")
28
+
29
+ def finishSession(self):
30
+ technique = self.sensorial_session.tecnica
31
+ technique.repeticion = 1
32
+ technique.save()
33
+ self.sensorial_session.activo = False
34
+ self.sensorial_session.save()
35
+ return self.sensorial_session
tecnicas/controllers/views_controller/session_management/monitor/monitor_pf_controller.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import Dato, Participacion, Catador, ListaPalabras, Producto
2
+ from tecnicas.controllers import SesionController
3
+ from .monitor_controller import MonitorController
4
+
5
+
6
+ class MonitorPFController(MonitorController):
7
+ def __init__(self, session: SesionController):
8
+ super().__init__(session)
9
+ self.url_view = "tecnicas/manage_sesions/monitor-session-pf.html"
10
+ self.previus_view = "cata_system:detalles_sesion"
11
+
12
+ def checkAllFinish(self):
13
+ rep = self.sensorial_session.tecnica.repeticion
14
+
15
+ finish_data = ()
16
+
17
+ if rep == 1 or rep == 2:
18
+ finish_data = self.checkFinishFirstPhase()
19
+ elif rep >= 3:
20
+ finish_data = self.checkFinishRepetition()
21
+
22
+ return finish_data
23
+
24
+ def checkFinishFirstPhase(self):
25
+ num_paricipations = Participacion.objects.filter(
26
+ tecnica=self.sensorial_session.tecnica).count()
27
+ if num_paricipations < self.sensorial_session.tecnica.limite_catadores:
28
+ return (False, "No se ha alcanzado el número máximo de catadores")
29
+
30
+ unfinished_participations = Participacion.objects.filter(
31
+ tecnica=self.sensorial_session.tecnica, finalizado=False).count()
32
+ if unfinished_participations:
33
+ return (False, "No todos los catadores han finalizado su evaluación")
34
+
35
+ return (True, "Puedes finalizar la sesión")
36
+
37
+ def checkFinishRepetition(self) -> tuple[bool, str]:
38
+ technique = self.sensorial_session.tecnica
39
+
40
+ # Revisar numero de catadores sea alcanzado
41
+ all_participations = list(
42
+ Participacion.objects.filter(tecnica=technique))
43
+ if len(all_participations) < technique.limite_catadores:
44
+ return (False, "No se ha alcanzado el número máximo de Catadores")
45
+
46
+ # Revisar que cada catador haya terminado de calificar sus palabras
47
+ for particiapation in all_participations:
48
+ expected_ratings_repetition = self.getExpectedRatings(
49
+ tester=particiapation.catador)
50
+
51
+ num_ratings_now = Dato.objects.filter(
52
+ id_calificacion__num_repeticion=technique.repeticion,
53
+ id_calificacion__id_catador=particiapation.catador,
54
+ id_calificacion__id_tecnica=technique
55
+ ).count()
56
+
57
+ if num_ratings_now < expected_ratings_repetition:
58
+ return (False, "No todos los catadores han finalizado su evaluación")
59
+
60
+ return (True, "Puedes finalizar la sesión")
61
+
62
+ def getExpectedRatings(self, tester: Catador):
63
+ num_words = ListaPalabras.objects.get(
64
+ tecnica=self.sensorial_session.tecnica,
65
+ catador=tester,
66
+ es_final=True
67
+ ).palabras.all().count()
68
+
69
+ num_products = Producto.objects.filter(
70
+ id_tecnica=self.sensorial_session.tecnica).count()
71
+
72
+ return num_products * num_words
tecnicas/controllers/views_controller/session_management/{monitor_rata_controller.py → monitor/monitor_rata_controller.py} RENAMED
@@ -1,6 +1,3 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import render, redirect
3
- from django.urls import reverse
4
  from tecnicas.models import Dato, Participacion
5
  from tecnicas.controllers import SesionController
6
  from .monitor_controller import MonitorController
@@ -12,23 +9,10 @@ class MonitorRATAController(MonitorController):
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))
 
 
 
 
1
  from tecnicas.models import Dato, Participacion
2
  from tecnicas.controllers import SesionController
3
  from .monitor_controller import MonitorController
 
9
  self.url_view = "tecnicas/manage_sesions/monitor-sesion.html"
10
  self.previus_view = "cata_system:detalles_sesion"
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  def checkAllFinish(self):
13
  technique = self.sensorial_session.tecnica
14
 
15
+ expected_ratings_repetition = self.getExpectedRatings()
16
 
17
  all_participations = list(
18
  Participacion.objects.filter(tecnica=technique))
tecnicas/controllers/views_controller/session_management/monitor/monitor_sort_controller.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tecnicas.models import SesionSensorial
2
+ from tecnicas.models import Participacion
3
+ from .monitor_controller import MonitorController
4
+
5
+
6
+ class MonitorSortController(MonitorController):
7
+ def __init__(self, session: SesionSensorial):
8
+ super().__init__(session)
9
+ self.url_view = "tecnicas/manage_sesions/monitor-session-sort.html"
10
+ self.previus_view = "cata_system:detalles_sesion"
11
+
12
+ def checkAllFinish(self):
13
+ technique = self.sensorial_session.tecnica
14
+
15
+ num_participations = Participacion.objects.filter(
16
+ tecnica=technique).count()
17
+
18
+ if num_participations < technique.limite_catadores:
19
+ return (False, "No se ha alcanzado el número máximo de catadores")
20
+
21
+ unfinished_participations = Participacion.objects.filter(
22
+ tecnica=technique, finalizado=False).count()
23
+
24
+ if unfinished_participations > 0:
25
+ return (False, "No todos los catadores han finalizado su evaluación")
26
+
27
+ return (True, "Puedes finalizar la sesión")
tecnicas/controllers/views_controller/sessions_tester/{init_session_tester_controller.py → init_session/init_session_controller.py} RENAMED
@@ -1,96 +1,23 @@
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(
@@ -117,7 +44,7 @@ class InitSessionTesterController():
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)
 
 
1
  from django.http import HttpRequest
2
  from django.shortcuts import render, redirect
3
  from django.urls import reverse
4
+ from django.db import transaction
5
+ from tecnicas.models import Catador, SesionSensorial, Orden, Participacion, Producto, EsAtributo, EsVocabulario, Dato, ListaPalabras
6
  from tecnicas.utils import controller_error, shuffleArray
7
 
8
 
9
+ class InitSessionController():
10
  tester: Catador
11
  session: SesionSensorial
12
  order: Orden | dict
13
+ current_direction: str
14
+ current_direction = "tecnicas/forms_tester/init_scales_test.html"
15
  escalas_direction = "cata_system:session_convencional"
16
 
17
  def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
18
  self.tester = user_tester
19
  self.session = sensorial_session
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def assignOrder(self):
22
  with transaction.atomic():
23
  orders_without_tester = list(Orden.objects.select_for_update().filter(
 
44
  return create
45
  return self.order_to_assign
46
 
47
+ def isEndedSession(self):
48
  try:
49
  participation = Participacion.objects.get(
50
  catador=self.tester, tecnica=self.session.tecnica)
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_escalas_controller.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import Catador, SesionSensorial, Orden
5
+ from tecnicas.controllers import ParticipacionController
6
+ from .init_session_controller import InitSessionController
7
+
8
+
9
+ class InitSessionEscalasController(InitSessionController):
10
+ tester: Catador
11
+ session: SesionSensorial
12
+ order: Orden | dict
13
+
14
+ def __init__(self, sensorial_session, user_tester):
15
+ super().__init__(sensorial_session, user_tester)
16
+ self.current_direction = "tecnicas/forms_tester/init_scales_test.html"
17
+ self.escalas_direction = "cata_system:session_convencional"
18
+ self.cata_direction = "cata_system:session_cata"
19
+
20
+ def controllGet(self, request: HttpRequest):
21
+ context = {
22
+ "session": self.session,
23
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
24
+ }
25
+
26
+ order = self.checkAndAssignOrder()
27
+ if isinstance(order, dict):
28
+ context["error"] = order["error"]
29
+ return render(request, self.current_direction, context)
30
+
31
+ is_end = self.isEndedSession()
32
+
33
+ request.session["id_order"] = order.id
34
+ context["has_ended"] = is_end
35
+
36
+ if is_end:
37
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
38
+
39
+ if "error" in request.GET:
40
+ context["error"] = request.GET["error"]
41
+
42
+ return render(request, self.current_direction, context)
43
+
44
+ def controllPost(self, request: HttpRequest):
45
+ context = {
46
+ "session": self.session,
47
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
48
+ }
49
+
50
+ if request.POST["action"] == "start_posting":
51
+ parameters = {
52
+ "code_sesion": self.session.codigo_sesion
53
+ }
54
+
55
+ is_end = self.isEndedSession()
56
+ if is_end:
57
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
58
+ return render(request, self.current_direction, context)
59
+
60
+ update_participation = ParticipacionController.enterSession(
61
+ tester=request.user.user_catador, session=self.session)
62
+ if isinstance(update_participation, dict):
63
+ context["error"] = update_participation["error"]
64
+ return render(request, self.current_direction, context)
65
+
66
+ request.session["id_participation"] = update_participation.id
67
+
68
+ if self.session.tecnica.tipo_tecnica.nombre_tecnica == "cata":
69
+ return redirect(reverse(self.cata_direction, kwargs=parameters))
70
+
71
+ return redirect(reverse(self.escalas_direction, kwargs=parameters))
72
+
73
+ elif request.POST["action"] == "exit_session":
74
+ response = ParticipacionController.outSession(
75
+ tester=request.user.user_catador, session=self.session)
76
+ if isinstance(response, dict):
77
+ context["error"] = response["error"]
78
+ return render(request, self.current_direction, context)
79
+
80
+ else:
81
+ context["error"] = "Acción sin especificar"
82
+ return render(request, self.current_direction, context)
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_napping_controller.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import Participacion, TecnicaModalidad
5
+ from tecnicas.controllers import ParticipacionController
6
+ from .init_session_controller import InitSessionController
7
+
8
+
9
+ class InitSessionNappingController(InitSessionController):
10
+ def __init__(self, sensorial_session, user_tester):
11
+ super().__init__(sensorial_session, user_tester)
12
+ self.current_direction = "tecnicas/forms_tester/init_test_napping.html"
13
+ self.napping_direction = "cata_system:session_napping"
14
+
15
+ def controllGet(self, request: HttpRequest):
16
+ self.context = {
17
+ "session": self.session,
18
+ "type_technique": "napping",
19
+ "has_ended": self.isEndedSession()
20
+ }
21
+
22
+ self.setStatusSession()
23
+
24
+ if "error" in request.GET:
25
+ self.context["error"] = request.GET["error"]
26
+
27
+ return render(request, self.current_direction, self.context)
28
+
29
+ def isEndedSession(self):
30
+ return Participacion.objects.get(
31
+ tecnica=self.session.tecnica, catador=self.tester).finalizado
32
+
33
+ def controllPost(self, request: HttpRequest):
34
+ context = {
35
+ "session": self.session,
36
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
37
+ }
38
+
39
+ use_action = request.POST["action"]
40
+
41
+ if use_action == "start_posting":
42
+ parameters = {
43
+ "code_sesion": self.session.codigo_sesion
44
+ }
45
+
46
+ is_end = self.isEndedSession()
47
+ if is_end:
48
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
49
+ return render(request, self.current_direction, context)
50
+
51
+ update_participation = ParticipacionController.enterSession(
52
+ tester=request.user.user_catador, session=self.session)
53
+
54
+ if isinstance(update_participation, dict):
55
+ context["error"] = update_participation["error"]
56
+ return render(request, self.current_direction, context)
57
+
58
+ request.session["id_participation"] = update_participation.id
59
+
60
+ return redirect(reverse(self.napping_direction, kwargs=parameters))
61
+
62
+ elif use_action == "exit_session":
63
+ response = ParticipacionController.outSession(
64
+ tester=request.user.user_catador, session=self.session)
65
+ if isinstance(response, dict):
66
+ context["error"] = response["error"]
67
+ return render(request, self.current_direction, context)
68
+
69
+ else:
70
+ context["error"] = "Acción sin especificar"
71
+ return render(request, self.current_direction, context)
72
+
73
+ def setStatusSession(self):
74
+ technique_mode = TecnicaModalidad.objects.get(
75
+ tecnica=self.session.tecnica).modalidad.nombre
76
+
77
+ if technique_mode == "sin modalidad":
78
+ self.context["status"] = "La sesión usa Napping"
79
+ else:
80
+ self.context["status"] = f"La sesión usa Napping con modalidad {technique_mode}"
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_pf_controller.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import Participacion, Producto, Dato, ListaPalabras
5
+ from tecnicas.controllers import ParticipacionController
6
+ from .init_session_controller import InitSessionController
7
+
8
+
9
+ class InitSessionPFController(InitSessionController):
10
+ def __init__(self, sensorial_session, user_tester):
11
+ super().__init__(sensorial_session, user_tester)
12
+ self.current_direction = "tecnicas/forms_tester/init_pf_test.html"
13
+ self.pf_direction = "cata_system:session_pf"
14
+
15
+ def controllGet(self, request: HttpRequest):
16
+ context = {
17
+ "session": self.session,
18
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
19
+ }
20
+
21
+ (is_end, message, rep_show) = self.isEndedSession()
22
+
23
+ context["has_ended"] = is_end
24
+ context["activity"] = message
25
+ context["repetition"] = rep_show
26
+
27
+ if "error" in request.GET:
28
+ context["error"] = request.GET["error"]
29
+
30
+ return render(request, self.current_direction, context)
31
+
32
+ def controllPost(self, request: HttpRequest):
33
+ context = {
34
+ "session": self.session,
35
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
36
+ }
37
+
38
+ action = request.POST["action"]
39
+
40
+ if action == "start_posting":
41
+ parameters = {
42
+ "code_sesion": self.session.codigo_sesion
43
+ }
44
+
45
+ (is_end, message, rep_show) = self.isEndedSession()
46
+ if is_end:
47
+ return self.controllGet(request)
48
+
49
+ update_participation = ParticipacionController.enterSession(
50
+ tester=request.user.user_catador, session=self.session)
51
+ if isinstance(update_participation, dict):
52
+ context["error"] = update_participation["error"]
53
+ return render(request, self.current_direction, context)
54
+
55
+ request.session["id_participation"] = update_participation.id
56
+
57
+ return redirect(reverse(self.pf_direction, kwargs=parameters))
58
+
59
+ elif action == "exit_session":
60
+ response = ParticipacionController.outSession(
61
+ tester=request.user.user_catador, session=self.session)
62
+ if isinstance(response, dict):
63
+ context["error"] = response["error"]
64
+ return self.controllGet(request)
65
+
66
+ else:
67
+ context["error"] = "Acción sin especificar"
68
+ return render(request, self.current_direction, context)
69
+
70
+ def isEndedSession(self) -> tuple[bool, str, int]:
71
+ rep = self.session.tecnica.repeticion
72
+
73
+ is_end = False
74
+ message = ""
75
+ repetitiom_show = 0
76
+
77
+ if rep == 1:
78
+ is_end = self.endedSessionMakeList()
79
+ message = "Ya has creado la Lista de palabras inicial" if is_end else "Debes crear tu lista de palabras inicial"
80
+ repetitiom_show = 0
81
+ elif rep == 2:
82
+ is_end = self.endedSessionMakeList()
83
+ message = "Ya has creado la Lista de palabras final" if is_end else "Debes crear tu lista de palabras final"
84
+ repetitiom_show = 0
85
+ elif rep >= 3:
86
+ is_end = self.endedSessionRepetition()
87
+ message = "Has finalizado con el proceso de calificación" if is_end else "Debe hacer tu proceso de calificación"
88
+ repetitiom_show = rep - 2
89
+ else:
90
+ message = "Parece que la repetición es cero, no es posible hacer algo ahora mismo"
91
+
92
+ return (is_end, message, repetitiom_show)
93
+
94
+ def endedSessionMakeList(self):
95
+ try:
96
+ return Participacion.objects.get(
97
+ catador=self.tester, tecnica=self.session.tecnica).finalizado
98
+ except Participacion.DoesNotExist:
99
+ print("No se ha encontrado la participación")
100
+ return False
101
+
102
+ def endedSessionRepetition(self):
103
+ try:
104
+ participation = Participacion.objects.get(
105
+ catador=self.tester, tecnica=self.session.tecnica)
106
+ self.session.refresh_from_db()
107
+
108
+ # ////////////////////////////////////////////////////////////// #
109
+ #
110
+ # numero_datos_esperadas = num_productos * num_palabras
111
+ # Si numero_datos_esperadas es igual a numero_datos_actuales en la repetcion R
112
+ # Ha terminado la repeticion
113
+ #
114
+ # ////////////////////////////////////////////////////////////// #
115
+
116
+ if participation.finalizado:
117
+ num_products = Producto.objects.filter(
118
+ id_tecnica=self.session.tecnica).count()
119
+
120
+ num_words = ListaPalabras.objects.get(
121
+ tecnica=self.session.tecnica,
122
+ catador=self.tester,
123
+ es_final=True
124
+ ).palabras.all().count()
125
+
126
+ expected_ratings_repetition = num_products * num_words
127
+
128
+ technique = self.session.tecnica
129
+ num_ratings_now = Dato.objects.filter(
130
+ id_calificacion__id_catador=self.tester,
131
+ id_calificacion__id_tecnica=technique,
132
+ id_calificacion__num_repeticion=technique.repeticion
133
+ ).count()
134
+
135
+ is_end = num_ratings_now >= expected_ratings_repetition
136
+
137
+ return is_end
138
+
139
+ else:
140
+ return participation.finalizado
141
+
142
+ except Participacion.DoesNotExist:
143
+ print("No se ha encontrado la participación")
144
+ return False
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_rata_controller.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render
3
+ from .init_session_controller import InitSessionController
4
+
5
+
6
+ class InitSessionRATAController(InitSessionController):
7
+ def __init__(self, sensorial_session, user_tester):
8
+ super().__init__(sensorial_session, user_tester)
9
+ self.current_direction = "tecnicas/forms_tester/init_scales_test.html"
10
+
11
+ def controllGet(self, request: HttpRequest):
12
+ context = {
13
+ "session": self.session,
14
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
15
+ }
16
+
17
+ is_end = self.isEndedSession()
18
+
19
+ context["has_ended"] = is_end
20
+
21
+ if is_end:
22
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
23
+
24
+ return render(request, self.current_direction, context)
tecnicas/controllers/views_controller/sessions_tester/init_session/init_session_sort_controller.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import Participacion
5
+ from tecnicas.controllers import ParticipacionController
6
+ from .init_session_controller import InitSessionController
7
+
8
+
9
+ class InitSessionSortController(InitSessionController):
10
+ def __init__(self, sensorial_session, user_tester):
11
+ super().__init__(sensorial_session, user_tester)
12
+ self.current_direction = "tecnicas/forms_tester/init_test_sort.html"
13
+ self.sort_direction = "cata_system:session_sort"
14
+
15
+ def controllGet(self, request: HttpRequest, error=""):
16
+ context = {
17
+ "session": self.session,
18
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
19
+ }
20
+
21
+ is_end = self.isEndedSession()
22
+
23
+ context["has_ended"] = is_end
24
+
25
+ if is_end:
26
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
27
+
28
+ if "error" in request.GET:
29
+ context["error"] = request.GET["error"]
30
+
31
+ return render(request, self.current_direction, context)
32
+
33
+ def isEndedSession(self):
34
+ participation = Participacion.objects.get(
35
+ catador=self.tester, tecnica=self.session.tecnica)
36
+
37
+ return participation.finalizado
38
+
39
+ def controllPost(self, request: HttpRequest):
40
+ context = {
41
+ "session": self.session,
42
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
43
+ }
44
+
45
+ use_action = request.POST["action"]
46
+
47
+ if use_action == "start_posting":
48
+ parameters = {
49
+ "code_sesion": self.session.codigo_sesion
50
+ }
51
+
52
+ is_end = self.isEndedSession()
53
+ if is_end:
54
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
55
+ return render(request, self.current_direction, context)
56
+
57
+ update_participation = ParticipacionController.enterSession(
58
+ tester=request.user.user_catador, session=self.session)
59
+
60
+ if isinstance(update_participation, dict):
61
+ context["error"] = update_participation["error"]
62
+ return render(request, self.current_direction, context)
63
+
64
+ request.session["id_participation"] = update_participation.id
65
+
66
+ return redirect(reverse(self.sort_direction, kwargs=parameters))
67
+
68
+ elif use_action == "exit_session":
69
+ response = ParticipacionController.outSession(
70
+ tester=request.user.user_catador, session=self.session)
71
+ if isinstance(response, dict):
72
+ context["error"] = response["error"]
73
+ return render(request, self.current_direction, context)
74
+
75
+ else:
76
+ context["error"] = "Acción sin especificar"
77
+ return render(request, self.current_direction, context)
tecnicas/controllers/views_controller/sessions_tester/login_session_tester_controller.py CHANGED
@@ -1,4 +1,4 @@
1
- from django.http import HttpRequest, JsonResponse
2
  from django.shortcuts import render, redirect
3
  from django.urls import reverse
4
  from django.db import transaction
@@ -12,6 +12,7 @@ class LoginSessionTesterController():
12
  taster_participation: Participacion
13
  current_direcction = "tecnicas/forms_tester/login_session.html"
14
  destinity_direcction = "cata_system:catador_init_session"
 
15
 
16
  def __init__(self):
17
  self.tester = Catador()
@@ -28,17 +29,18 @@ class LoginSessionTesterController():
28
  return controller_error("Credenciales inválidas")
29
 
30
  def validateEntryEscalas(self, request=HttpRequest):
31
- context = {}
32
  if not self.session.activo:
33
- context["error"] = "La sesión no está activa actualmente"
34
- return render(request, self.current_direcction, context)
35
 
36
  if self.session.tecnica.repeticion == 1:
37
  try:
38
  self.taster_participation = Participacion.objects.get(
39
  tecnica=self.session.tecnica, catador=self.tester)
40
- context["error"] = "Usted ya esta dentro de la sesión"
41
- return render(request, self.current_direcction, context)
 
42
  except Participacion.DoesNotExist:
43
  with transaction.atomic():
44
  code_session = self.session.codigo_sesion
@@ -50,8 +52,8 @@ class LoginSessionTesterController():
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,
@@ -63,21 +65,21 @@ class LoginSessionTesterController():
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 = {}
71
  if not self.session.activo:
72
- context["error"] = "La sesión no está activa actualmente"
73
- return render(request, self.current_direcction, context)
74
 
75
  if self.session.tecnica.repeticion <= 1:
76
  try:
77
  self.taster_participation = Participacion.objects.get(
78
  tecnica=self.session.tecnica, catador=self.tester)
79
- context["error"] = "Usted ya esta dentro de la sesión"
80
- return render(request, self.current_direcction, context)
81
  except Participacion.DoesNotExist:
82
  with transaction.atomic():
83
  code_session = self.session.codigo_sesion
@@ -95,5 +97,67 @@ class LoginSessionTesterController():
95
  }
96
  return redirect(reverse(self.destinity_direcction, kwargs=params))
97
  else:
98
- context["error"] = "Imposible acceder a esta sesión"
99
- return render(request, self.current_direcction, context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
  from django.shortcuts import render, redirect
3
  from django.urls import reverse
4
  from django.db import transaction
 
12
  taster_participation: Participacion
13
  current_direcction = "tecnicas/forms_tester/login_session.html"
14
  destinity_direcction = "cata_system:catador_init_session"
15
+ context = {}
16
 
17
  def __init__(self):
18
  self.tester = Catador()
 
29
  return controller_error("Credenciales inválidas")
30
 
31
  def validateEntryEscalas(self, request=HttpRequest):
32
+ self.context = {}
33
  if not self.session.activo:
34
+ self.context["error"] = "La sesión no está activa actualmente"
35
+ return render(request, self.current_direcction, self.context)
36
 
37
  if self.session.tecnica.repeticion == 1:
38
  try:
39
  self.taster_participation = Participacion.objects.get(
40
  tecnica=self.session.tecnica, catador=self.tester)
41
+ self.context["error"] = "Usted ya esta dentro de la sesión"
42
+ return render(request, self.current_direcction, self.context)
43
+
44
  except Participacion.DoesNotExist:
45
  with transaction.atomic():
46
  code_session = self.session.codigo_sesion
 
52
  tecnica=self.session.tecnica).count()
53
 
54
  if current_num_testers >= max_testers:
55
+ self.context["error"] = "La sesión ha alcanzado el número máximo de catadores"
56
+ return render(request, self.current_direcction, self.context)
57
 
58
  self.taster_participation = Participacion.objects.create(
59
  tecnica=self.session.tecnica,
 
65
  }
66
  return redirect(reverse(self.destinity_direcction, kwargs=params))
67
  else:
68
+ self.context["error"] = "Ya no es posible ingresar a la sesión"
69
+ return render(request, self.current_direcction, self.context)
70
 
71
+ def validateEntryRataCata(self, request: HttpRequest):
72
+ self.context = {}
73
  if not self.session.activo:
74
+ self.context["error"] = "La sesión no está activa actualmente"
75
+ return render(request, self.current_direcction, self.context)
76
 
77
  if self.session.tecnica.repeticion <= 1:
78
  try:
79
  self.taster_participation = Participacion.objects.get(
80
  tecnica=self.session.tecnica, catador=self.tester)
81
+ self.context["error"] = "Usted ya esta dentro de la sesión"
82
+ return render(request, self.current_direcction, self.context)
83
  except Participacion.DoesNotExist:
84
  with transaction.atomic():
85
  code_session = self.session.codigo_sesion
 
97
  }
98
  return redirect(reverse(self.destinity_direcction, kwargs=params))
99
  else:
100
+ self.context["error"] = "Imposible acceder a esta sesión"
101
+ return render(request, self.current_direcction, self.context)
102
+
103
+ def validateEntryLimitTesters(self, request: HttpRequest):
104
+ self.context = {}
105
+ if not self.session.activo:
106
+ self.context["error"] = "La sesión no está activa actualmente"
107
+ return render(request, self.current_direcction, self.context)
108
+
109
+ if self.session.tecnica.repeticion == 1:
110
+ return self.entrySessionLimitTesters(request)
111
+
112
+ else:
113
+ self.context["error"] = "Ya no es posible ingresar a la sesión"
114
+ return render(request, self.current_direcction, self.context)
115
+
116
+ def validateEntryNapping(self, request: HttpRequest):
117
+ self.context = {}
118
+ if not self.session.activo:
119
+ self.context["error"] = "La sesión no está activa actualmente"
120
+ return render(request, self.current_direcction, self.context)
121
+
122
+ if self.session.tecnica.repeticion == 0:
123
+ return self.entrySessionLimitTesters(request)
124
+
125
+ else:
126
+ self.context["error"] = "Ya no es posible ingresar a la sesión"
127
+ return render(request, self.current_direcction, self.context)
128
+
129
+ def entrySessionLimitTesters(self, request: HttpRequest):
130
+ try:
131
+ self.taster_participation = Participacion.objects.get(
132
+ tecnica=self.session.tecnica, catador=self.tester)
133
+ self.context["error"] = "Usted ya esta dentro de la sesión"
134
+ return render(request, self.current_direcction, self.context)
135
+
136
+ except Participacion.DoesNotExist:
137
+ try:
138
+ with transaction.atomic():
139
+ code_session = self.session.codigo_sesion
140
+ self.session = SesionSensorial.objects.select_for_update().get(
141
+ codigo_sesion=code_session)
142
+
143
+ max_testers = self.session.tecnica.limite_catadores
144
+ current_num_testers = Participacion.objects.filter(
145
+ tecnica=self.session.tecnica).count()
146
+
147
+ if current_num_testers >= max_testers:
148
+ raise ValueError(
149
+ "La sesión ha alcanzado el número máximo de catadores")
150
+
151
+ self.taster_participation = Participacion.objects.create(
152
+ tecnica=self.session.tecnica,
153
+ catador=self.tester,
154
+ finalizado=False
155
+ )
156
+ params = {
157
+ "code_sesion": self.session.codigo_sesion
158
+ }
159
+ return redirect(reverse(self.destinity_direcction, kwargs=params))
160
+
161
+ except ValueError as e:
162
+ self.context["error"] = str(e)
163
+ return render(request, self.current_direcction, self.context)
tecnicas/controllers/views_controller/sessions_tester/tests_forms/general_test_controller.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.models import SesionSensorial, Catador, Participacion
5
+ from tecnicas.controllers import ParticipacionController
6
+
7
+
8
+ class GenetalTestController():
9
+ previus_directory = "cata_system:catador_init_session"
10
+ context = {}
11
+ current_directory: str
12
+
13
+ def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
14
+ self.tester = user_tester
15
+ self.session = sensorial_session
16
+
17
+ def controllPost(self, request: HttpRequest):
18
+ action = request.POST["action"]
19
+
20
+ if action == "finish_session":
21
+ self.participation = Participacion.objects.get(
22
+ tecnica=self.session.tecnica, catador=request.user.user_catador)
23
+ ParticipacionController.finishSession(self.participation)
24
+ params = {"code_sesion": self.session.codigo_sesion}
25
+ return redirect(reverse(self.previus_directory, kwargs=params))
26
+
27
+ else:
28
+ return self.controllGet(request, error="Acción no permitida")
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_cata_controller.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.models import Producto, Participacion, Palabra, Calificacion
5
+ from tecnicas.controllers import PalabrasController, ParticipacionController
6
+ from .general_test_controller import GenetalTestController
7
+
8
+
9
+ class TestCataController(GenetalTestController):
10
+ def __init__(self, sensorial_session, user_tester):
11
+ super().__init__(sensorial_session, user_tester)
12
+ self.current_directory = "tecnicas/forms_tester/test_cata.html"
13
+
14
+ def controllGet(self, request: HttpRequest):
15
+ technique = self.session.tecnica
16
+ self.participation = Participacion.objects.get(
17
+ tecnica=technique, catador=request.user.user_catador)
18
+
19
+ self.context["session"] = self.session
20
+
21
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
22
+
23
+ words = PalabrasController.getWordsInTechnique(technique=technique)
24
+
25
+ use_product: Producto = None
26
+ use_words: list[Palabra] = None
27
+
28
+ # Revisamos el producto que le falten calificaciones
29
+ for current_product in products_in_technique:
30
+ try:
31
+ rating = Calificacion.objects.get(
32
+ num_repeticion=technique.repeticion,
33
+ id_producto=current_product,
34
+ id_tecnica=technique,
35
+ id_catador=self.tester
36
+ )
37
+ except Calificacion.DoesNotExist:
38
+ # Si no hay calificacion mandamos el producto actual y todas la palabras
39
+ use_product = current_product
40
+ use_words = words
41
+ break
42
+
43
+ # Si no hay producto que falta por calificar finalizar sesion para el Catador
44
+ if not use_product:
45
+ updated_participation = ParticipacionController.finishSession(
46
+ self.participation)
47
+ params = {
48
+ "code_sesion": self.session.codigo_sesion
49
+ }
50
+ return redirect(reverse(self.previus_directory, kwargs=params))
51
+
52
+ self.context["product"] = use_product
53
+ self.context["words"] = use_words
54
+
55
+ return render(request, self.current_directory, self.context)
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_napping_controller.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from django.db.models import F
5
+ from tecnicas.models import Participacion, Producto, TecnicaModalidad, DatoPunto, Calificacion, Modalidad, Palabra, GrupoProducto
6
+ from tecnicas.forms import ListWordsForm
7
+ from tecnicas.utils import noValidTechnique
8
+ from tecnicas.controllers import ParticipacionController
9
+ from .general_test_controller import GenetalTestController
10
+
11
+
12
+ class TestNappingController(GenetalTestController):
13
+ def __init__(self, sensorial_session, user_tester):
14
+ super().__init__(sensorial_session, user_tester)
15
+ self.napping_test = "tecnicas/forms_tester/test_napping.html"
16
+ self.napping_puf_test = "tecnicas/forms_tester/test_napping_puf.html"
17
+ self.sort_direction = "tecnicas/forms_tester/test_napping_sort.html"
18
+
19
+ def controllGet(self, request: HttpRequest):
20
+ technique = self.session.tecnica
21
+
22
+ self.participation = Participacion.objects.get(
23
+ tecnica=technique, catador=request.user.user_catador)
24
+
25
+ # Comprobar que el Catador no haya finalizado
26
+ if self.participation.finalizado:
27
+ params = {
28
+ "code_sesion": self.session.codigo_sesion
29
+ }
30
+ return redirect(reverse(self.previus_directory, kwargs=params))
31
+
32
+ name_mode_activate = TecnicaModalidad.objects.get(
33
+ tecnica=technique).modalidad.nombre
34
+
35
+ if name_mode_activate == "sin modalidad":
36
+ self.context["mode"] = "sin modalidad"
37
+ return self.nappingTest(request)
38
+
39
+ if name_mode_activate == "perfil ultra flash":
40
+ self.context["mode"] = "perfil ultra flash"
41
+ return self.nappingPufTest(request)
42
+
43
+ if name_mode_activate == "sorting":
44
+ self.context["mode"] = "sorting"
45
+ return self.nappingSort(request)
46
+
47
+ else:
48
+ return noValidTechnique(
49
+ name_view=self.previus_directory,
50
+ query_params={
51
+ "error": f"Trabajando en la modalidad: {name_mode_activate}"
52
+ },
53
+ params={
54
+ "code_sesion": self.session.codigo_sesion
55
+ }
56
+ )
57
+
58
+ def nappingTest(self, request: HttpRequest):
59
+ self.context["session"] = self.session
60
+ technique = self.session.tecnica
61
+
62
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
63
+ self.context["products"] = products_in_technique
64
+
65
+ self.setCoordinates()
66
+
67
+ return render(request, self.napping_test, self.context)
68
+
69
+ def nappingPufTest(self, request: HttpRequest):
70
+ maked_previus_napping = TecnicaModalidad.objects.get(
71
+ tecnica=self.session.tecnica)
72
+
73
+ self.context["maked_napping"] = True if maked_previus_napping else False
74
+ self.context["mode"] = "perfil ultra flash"
75
+ self.context["form"] = ListWordsForm()
76
+
77
+ self.context["session"] = self.session
78
+ technique = self.session.tecnica
79
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
80
+ self.context["products"] = products_in_technique
81
+ self.setCoordinates()
82
+ self.setWords()
83
+
84
+ return render(request, self.napping_puf_test, self.context)
85
+
86
+ def nappingSort(self, request: HttpRequest):
87
+ self.context["session"] = self.session
88
+ technique = self.session.tecnica
89
+
90
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
91
+ self.context["products"] = products_in_technique
92
+
93
+ self.context["form"] = ListWordsForm()
94
+ self.setCoordinates()
95
+ self.setGroups()
96
+
97
+ return render(request, self.sort_direction, self.context)
98
+
99
+ def setCoordinates(self):
100
+ technique = self.session.tecnica
101
+
102
+ ratings = Calificacion.objects.filter(
103
+ num_repeticion=0,
104
+ id_tecnica=technique,
105
+ id_catador=self.participation.catador
106
+ )
107
+
108
+ data_points = DatoPunto.objects.filter(
109
+ calificacion__in=ratings
110
+ ).values(
111
+ code=F("calificacion__id_producto__codigoProducto"),
112
+ px=F("x"),
113
+ py=F("y"),
114
+ id_product=F("calificacion__id_producto__id")
115
+ )
116
+
117
+ self.context["data_points"] = list(data_points)
118
+
119
+ def setGroups(self):
120
+ technique = self.session.tecnica
121
+
122
+ # Get all product groups for this tester
123
+ grupos_producto = GrupoProducto.objects.filter(
124
+ tecnica=technique,
125
+ catador=self.participation.catador
126
+ ).prefetch_related('productos', 'palabras')
127
+
128
+ groups = []
129
+ for group in grupos_producto:
130
+ # Get products in this group
131
+ products_list = []
132
+ for product in group.productos.all():
133
+ products_list.append({
134
+ 'id': product.id,
135
+ 'codigoProducto': product.codigoProducto
136
+ })
137
+
138
+ # Get words for this group
139
+ words_list = list(group.palabras.values_list(
140
+ 'nombre_palabra', flat=True))
141
+
142
+ groups.append({
143
+ 'id': group.id,
144
+ 'products': products_list,
145
+ 'words': words_list
146
+ })
147
+
148
+ self.context["groups"] = groups
149
+
150
+ def setWords(self):
151
+ technique = self.session.tecnica
152
+
153
+ ratings = Calificacion.objects.filter(
154
+ num_repeticion=0,
155
+ id_tecnica=technique,
156
+ id_catador=self.participation.catador
157
+ ).prefetch_related('palabras', 'id_producto')
158
+
159
+ words_by_product = {}
160
+ for rating in ratings:
161
+ product_code = rating.id_producto.codigoProducto
162
+ words_list = list(rating.palabras.values_list(
163
+ 'nombre_palabra', flat=True))
164
+ if words_list:
165
+ words_by_product[product_code] = words_list
166
+
167
+ self.context["words_by_product"] = words_by_product
168
+
169
+ def controllPost(self, request: HttpRequest):
170
+ action = request.POST.get("action")
171
+
172
+ if action == "finish_session":
173
+ # Get technique and mode
174
+ technique = self.session.tecnica
175
+ self.participation = Participacion.objects.get(
176
+ tecnica=technique, catador=request.user.user_catador)
177
+
178
+ name_mode_activate = TecnicaModalidad.objects.get(
179
+ tecnica=technique).modalidad.nombre
180
+
181
+ # Validate based on mode
182
+ validation_error = self.validateSessionCompletion(
183
+ technique, name_mode_activate)
184
+
185
+ if validation_error:
186
+ # Return to the appropriate template with error
187
+ if name_mode_activate == "sin modalidad":
188
+ return self.nappingTest(request)
189
+ elif name_mode_activate == "perfil ultra flash":
190
+ return self.nappingPufTest(request)
191
+
192
+ # If validation passes, finish the session
193
+ ParticipacionController.finishSession(self.participation)
194
+ params = {"code_sesion": self.session.codigo_sesion}
195
+ return redirect(reverse(self.previus_directory, kwargs=params))
196
+
197
+ # For other actions, call parent's controllPost
198
+ return super().controllPost(request)
199
+
200
+ def validateSessionCompletion(self, technique, mode_name):
201
+ # Get all products in technique
202
+ products = Producto.objects.filter(id_tecnica=technique)
203
+ product_count = products.count()
204
+
205
+ # Get all ratings for this tester
206
+ ratings = Calificacion.objects.filter(
207
+ num_repeticion=0,
208
+ id_tecnica=technique,
209
+ id_catador=self.participation.catador
210
+ ).select_related('id_producto').prefetch_related('palabras')
211
+
212
+ # Check if all products have ratings
213
+ if ratings.count() != product_count:
214
+ missing_count = product_count - ratings.count()
215
+ return f"Faltan {missing_count} producto(s) por evaluar."
216
+
217
+ # Check if all ratings have DatoPunto (coordinates)
218
+ ratings_with_points = DatoPunto.objects.filter(
219
+ calificacion__in=ratings
220
+ ).values_list('calificacion_id', flat=True)
221
+
222
+ ratings_without_points = ratings.exclude(id__in=ratings_with_points)
223
+ if ratings_without_points.exists():
224
+ missing_products = [
225
+ r.id_producto.codigoProducto for r in ratings_without_points
226
+ ]
227
+ return f"Los siguientes productos no tienen coordenadas: {', '.join(missing_products)}"
228
+
229
+ # Additional validation for "perfil ultra flash" mode
230
+ if mode_name == "perfil ultra flash":
231
+ # Check that each rating has at least one word
232
+ ratings_without_words = []
233
+ for rating in ratings:
234
+ if rating.palabras.count() < 1:
235
+ ratings_without_words.append(
236
+ rating.id_producto.codigoProducto)
237
+
238
+ if ratings_without_words:
239
+ return f"Los siguientes productos deben tener al menos 1 palabra: {', '.join(ratings_without_words)}"
240
+
241
+ # All validations passed
242
+ return None
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_pf_controller.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.models import Producto, Participacion, Palabra, Calificacion, ListaPalabras, Dato
5
+ from tecnicas.controllers import ParticipacionController, PalabrasController, EscalaController
6
+ from tecnicas.forms import ListWordsForm
7
+ from .general_test_controller import GenetalTestController
8
+
9
+
10
+ class TestPFController(GenetalTestController):
11
+ skip_phases = 2
12
+
13
+ def __init__(self, sensorial_session, user_tester):
14
+ super().__init__(sensorial_session, user_tester)
15
+
16
+ def controllGet(self, request: HttpRequest, error=""):
17
+ self.participation = Participacion.objects.get(
18
+ tecnica=self.session.tecnica, catador=request.user.user_catador)
19
+ self.context["session"] = self.session
20
+
21
+ if error:
22
+ self.context["error"] = error
23
+
24
+ rep = self.session.tecnica.repeticion
25
+
26
+ if rep == 1:
27
+ self.current_directory = "tecnicas/forms_tester/test_pf_list_words.html"
28
+ response = self.getFirstPhase(request)
29
+ elif rep == 2:
30
+ self.current_directory = "tecnicas/forms_tester/test_pf_list_words.html"
31
+ response = self.getSecondPhase(request)
32
+ elif rep >= 3:
33
+ self.current_directory = "tecnicas/forms_tester/test_pf_rating_list.html"
34
+ response = self.getRepetitionPhase(request)
35
+ else:
36
+ response = self.getErrorRepetition(request)
37
+
38
+ return response
39
+
40
+ def controllPost(self, request: HttpRequest):
41
+ action = request.POST["action"]
42
+
43
+ if action == "finish_session":
44
+ self.participation = Participacion.objects.get(
45
+ tecnica=self.session.tecnica, catador=request.user.user_catador)
46
+ ParticipacionController.finishSession(self.participation)
47
+ params = {"code_sesion": self.session.codigo_sesion}
48
+ return redirect(reverse(self.previus_directory, kwargs=params))
49
+
50
+ else:
51
+ return self.controllGet(request, error="Acción no permitida")
52
+
53
+ def getFirstPhase(self, request: HttpRequest):
54
+ self.participation.refresh_from_db()
55
+
56
+ if self.participation.finalizado:
57
+ params = {
58
+ "code_sesion": self.session.codigo_sesion
59
+ }
60
+ return redirect(reverse(self.previus_directory, kwargs=params))
61
+
62
+ self.context["form"] = ListWordsForm()
63
+ self.context["initial_phase"] = True
64
+
65
+ try:
66
+ tester_list = ListaPalabras.objects.get(
67
+ tecnica=self.session.tecnica,
68
+ catador=request.user.user_catador,
69
+ es_final=False
70
+ )
71
+ list_words = list(tester_list.palabras.all())
72
+ self.context["words"] = list_words
73
+ except ListaPalabras.DoesNotExist:
74
+ self.context["words"] = []
75
+
76
+ return render(request, self.current_directory, self.context)
77
+
78
+ def getSecondPhase(self, request: HttpRequest):
79
+ self.participation.refresh_from_db()
80
+
81
+ if self.participation.finalizado:
82
+ params = {
83
+ "code_sesion": self.session.codigo_sesion
84
+ }
85
+ return redirect(reverse(self.previus_directory, kwargs=params))
86
+
87
+ try:
88
+ tester_list = ListaPalabras.objects.get(
89
+ tecnica=self.session.tecnica,
90
+ catador=request.user.user_catador,
91
+ es_final=True
92
+ )
93
+ except ListaPalabras.DoesNotExist:
94
+ tester_list = ListaPalabras.objects.get(
95
+ tecnica=self.session.tecnica,
96
+ catador=request.user.user_catador,
97
+ es_final=False
98
+ )
99
+
100
+ list_words = list(tester_list.palabras.all())
101
+
102
+ self.context["form"] = ListWordsForm()
103
+ self.context["initial_phase"] = False
104
+ self.context["words"] = list_words
105
+
106
+ return render(request, self.current_directory, self.context)
107
+
108
+ def getRepetitionPhase(self, request: HttpRequest):
109
+ '''
110
+ - Obtener todos los productos que se evaluan en la tecnica
111
+ - Obtener todas las palabras de la lista de palabras del catador
112
+ - Para cada palabra, comprobar que el numero de Dato sea igual al numero de Productos
113
+ - Si no hay datos mandar esa palabra por contexto con todos los productos
114
+ - De las palabras que falten tomar la primera y mandarla en el contexto
115
+ - Mandar todos los productos por el contexto
116
+ Nota: Para esta fase no hay necesidad de mandar una escala, la escala se creara en el cliente
117
+ '''
118
+ self.participation.refresh_from_db()
119
+
120
+ if self.participation.finalizado:
121
+ params = {"code_sesion": self.session.codigo_sesion}
122
+ return redirect(reverse(self.previus_directory, kwargs=params))
123
+
124
+ technique = self.session.tecnica
125
+
126
+ # Obtener todos los productos que se evaluan en la técnica
127
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
128
+
129
+ # Obtener todas las palabras de la lista del catador (preferir lista final)
130
+ try:
131
+ words = list(ListaPalabras.objects.get(
132
+ tecnica=self.session.tecnica,
133
+ catador=request.user.user_catador,
134
+ es_final=True
135
+ ).palabras.all())
136
+ except ListaPalabras.DoesNotExist:
137
+ words = []
138
+
139
+ use_word: Palabra = None
140
+
141
+ # Revisar que palabra no ha sido calificada en todos los productos
142
+ for word in words:
143
+ current_num_data = Dato.objects.filter(
144
+ id_calificacion__num_repeticion=technique.repeticion,
145
+ id_calificacion__id_tecnica=technique,
146
+ id_calificacion__id_catador=request.user.user_catador,
147
+ id_palabra=word
148
+ ).count()
149
+
150
+ if not current_num_data:
151
+ use_word = word
152
+ break
153
+ elif current_num_data < len(products_in_technique):
154
+ self.context["error"] = "Se ha detectado una inconsistencia en los datos que se deben calificar, algunos productos no han sido calificados"
155
+ return render(request, self.current_directory, self.context)
156
+
157
+ if not use_word:
158
+ self.participation = Participacion.objects.get(
159
+ tecnica=self.session.tecnica, catador=request.user.user_catador)
160
+ ParticipacionController.finishSession(self.participation)
161
+ params = {"code_sesion": self.session.codigo_sesion}
162
+ return redirect(reverse(self.previus_directory, kwargs=params))
163
+
164
+ self.context["word"] = use_word
165
+ self.context["products"] = products_in_technique
166
+ self.context["repetition"] = technique.repeticion - self.skip_phases
167
+
168
+ return render(request, self.current_directory, self.context)
169
+
170
+ def getErrorRepetition(self, request: HttpRequest):
171
+ params = {
172
+ "code_sesion": self.session.codigo_sesion
173
+ }
174
+ return redirect(reverse(self.previus_directory, kwargs=params))
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_rata_controller.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect, render
3
+ from django.urls import reverse
4
+ from tecnicas.models import Participacion, Producto, Calificacion, Palabra
5
+ from tecnicas.controllers import ParticipacionController, PalabrasController, EscalaController
6
+ from .general_test_controller import GenetalTestController
7
+
8
+
9
+ class TestRataController(GenetalTestController):
10
+ def __init__(self, sensorial_session, user_tester):
11
+ super().__init__(sensorial_session, user_tester)
12
+ self.current_directory = "tecnicas/forms_tester/test_convencional.html"
13
+
14
+ def controllGet(self, request: HttpRequest):
15
+ technique = self.session.tecnica
16
+ self.participation = Participacion.objects.get(
17
+ tecnica=technique, catador=request.user.user_catador)
18
+
19
+ self.context["session"] = self.session
20
+
21
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
22
+
23
+ words = PalabrasController.getWordsInTechnique(technique=technique)
24
+
25
+ use_product: Producto = None
26
+ use_words: list[Palabra] = None
27
+
28
+ # Revisamos el producto que le falten calificaciones
29
+ for current_product in products_in_technique:
30
+ try:
31
+ rating = Calificacion.objects.get(
32
+ num_repeticion=technique.repeticion,
33
+ id_producto=current_product,
34
+ id_tecnica=technique,
35
+ id_catador=self.tester
36
+ )
37
+ except Calificacion.DoesNotExist:
38
+ # Si no hay calificacion mandamos el producto actual y todas la palabras
39
+ use_product = current_product
40
+ use_words = words
41
+ break
42
+
43
+ # Obtener los datos asociados para la calificacion para ver que palabras quedan por calificar
44
+ recoreded_data = rating.dato_calificacion.all()
45
+
46
+ if not recoreded_data:
47
+ # Si no hay datos entonces devolver el producto con todas las palabras
48
+ use_product = current_product
49
+ use_words = words
50
+ break
51
+ else:
52
+ words_to_use = PalabrasController.getWordsWithoutData(
53
+ recoreded_data=recoreded_data, words=words)
54
+
55
+ # Si quedan palabras por calificar mandar las palabras con el producto
56
+ if not isinstance(words_to_use, dict) and words_to_use:
57
+ use_product = current_product
58
+ use_words = words_to_use
59
+ break
60
+
61
+ # Si no hay producto que falta por calificar finalizar sesion para el Catador
62
+ if not use_product:
63
+ updated_participation = ParticipacionController.finishSession(
64
+ self.participation)
65
+ params = {
66
+ "code_sesion": self.session.codigo_sesion
67
+ }
68
+ return redirect(reverse(self.previus_directory, kwargs=params))
69
+
70
+ self.context["product"] = use_product
71
+ self.context["words"] = use_words
72
+
73
+ # Agregar informacion de la escala
74
+ scale = EscalaController.getScaleByTechnique(technique=technique)
75
+ self.context["scale"] = scale
76
+ self.context["type_scale"] = scale.id_tipo_escala.nombre_escala
77
+
78
+ use_tags = EscalaController.getRelatedTagsInScale(scale=scale)
79
+ self.context["tags"] = use_tags
80
+
81
+ if self.context["type_scale"] == "continua":
82
+ self.context["size_scale"] = {
83
+ "max_size": scale.longitud * 100,
84
+ "middle_size": (scale.longitud * 100)/2
85
+ }
86
+
87
+ return render(request, self.current_directory, self.context)