- {{ error }} -
-diff --git a/tecnicas/constants.py b/tecnicas/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ad46edecc425aea269cf8c0827b969671e487d4
--- /dev/null
+++ b/tecnicas/constants.py
@@ -0,0 +1,6 @@
+FORMS_TO_CREATE_SESSION = [
+ "form_basic",
+ "form_tags",
+ "form_codes",
+ "form_words"
+]
diff --git a/tecnicas/controllers/__init__.py b/tecnicas/controllers/__init__.py
index c3a9b62260fdba138175f771e866a66e5c8e10b4..41d8a06586110a47b5df41ef29756f8fa418421c 100644
--- a/tecnicas/controllers/__init__.py
+++ b/tecnicas/controllers/__init__.py
@@ -12,8 +12,20 @@ from .models_controller.posicion_controller import PosicionController
from .models_controller.particiapacion_controller import ParticipacionController
from .models_controller.dato_controller import DatoController
-from .views_controller.detalles_sesion_controller import DetallesSesionController
from .views_controller.login_tester_controller import LoginTesterController
from .views_controller.main_tester_form_controller import MainTesterFormController
from .views_controller.api_rating_controller import ApiRatingController
-from .views_controller.monitor_sesion_controller import MonitorSesionController
\ No newline at end of file
+from .views_controller.tester_list_controller import TesterListController
+from .views_controller.list_sessions_tester_controller import ListSessionsTesterController
+
+from .views_controller.create_session.panel_basic_controller import PanelBasicController
+from .views_controller.create_session.panel_tags_controller import PanelTagsController
+from .views_controller.create_session.panel_codes_controller import PanelCodesController
+from .views_controller.create_session.panel_words_controller import PanelWordsController
+from .views_controller.create_session.panel_create_controller import PanelCreateController
+
+from .views_controller.session_management.details_controller import DetallesController
+from .views_controller.session_management.details_escala_controller import DetallesEscalasController
+from .views_controller.session_management.details_rata_controller import DetallesRATAController
+from .views_controller.session_management.monitor_controller import MonitorController
+from .views_controller.session_management.monitor_escalas_controller import MonitorEscalasController
\ No newline at end of file
diff --git a/tecnicas/controllers/models_controller/calificacion_controller.py b/tecnicas/controllers/models_controller/calificacion_controller.py
index f9e6b98f5b8ac8c6eaf3eca28d26a1e54a7faeda..a99f63d3ddc9ff1d3326a59e82656648a8dee480 100644
--- a/tecnicas/controllers/models_controller/calificacion_controller.py
+++ b/tecnicas/controllers/models_controller/calificacion_controller.py
@@ -1,8 +1,7 @@
from django.core.exceptions import ValidationError
-from django.db import DatabaseError
from collections import defaultdict
-from ...models import Calificacion, Tecnica, Posicion, Producto, Catador
-from ...utils import controller_error, getId
+from tecnicas.models import Calificacion, Tecnica, Posicion, Producto, Catador
+from tecnicas.utils import controller_error, getId
class CalificacionController():
@@ -46,7 +45,7 @@ class CalificacionController():
repetition = technique.repeticion
if not repetition:
- return {"error": "sin datos calficados aun"}
+ return controller_error("Sin datos calificados aún")
ratings = list(Calificacion.objects.filter(id_tecnica=technique))
@@ -84,7 +83,7 @@ class CalificacionController():
elif id_tester is not None:
filters["id_catador__id"] = id_tester
elif user_tester is not None:
- filters["id_catador__usuarioCatador"] = user_tester
+ filters["id_catador__user__username"] = user_tester
ratings = list(Calificacion.objects.filter(**filters).select_related(
"id_producto",
diff --git a/tecnicas/controllers/models_controller/dato_controller.py b/tecnicas/controllers/models_controller/dato_controller.py
index b5d7016cd72d5ed89aa039c3cf5e366869b5500d..019cd4d731ff2a5e5f9efec921475995276fc89e 100644
--- a/tecnicas/controllers/models_controller/dato_controller.py
+++ b/tecnicas/controllers/models_controller/dato_controller.py
@@ -89,7 +89,7 @@ class DatoController():
producto_code=F(
"id_dato__id_calificacion__id_producto__codigoProducto"),
usuarioCatador=F(
- "id_dato__id_calificacion__id_catador__usuarioCatador"),
+ "id_dato__id_calificacion__id_catador__user__username"),
dato_valor=F("valor")
)
)
diff --git a/tecnicas/controllers/models_controller/escala_controller.py b/tecnicas/controllers/models_controller/escala_controller.py
index 3b2a36a1dd4dba74ede1fa223a546e2e07f532ad..89a7b50c388b7f6d36f1d8ec694284ed957210dc 100644
--- a/tecnicas/controllers/models_controller/escala_controller.py
+++ b/tecnicas/controllers/models_controller/escala_controller.py
@@ -26,7 +26,7 @@ class EscalaController():
self.scale.save()
return self.scale
except DatabaseError as error:
- return controller_error("error al guardar la escala")
+ return controller_error("Error al guardar la escala")
def deleteScale(self):
self.scale.delete()
@@ -67,7 +67,7 @@ class EscalaController():
return self.tags_relation
except DatabaseError as error:
self.deleteRelationshipsWithLabels()
- return controller_error("error guardar relacion etiqueta escala")
+ return controller_error("Error en guardar la relación etiqueta escala")
@staticmethod
def getScaleByTechnique(technique: Tecnica = None, id_technique: int = None):
diff --git a/tecnicas/controllers/models_controller/ordenes_controller.py b/tecnicas/controllers/models_controller/ordenes_controller.py
index 367f0345a5446c33ddf430642d1c1909971e5578..0434e6a15664b0be05f7b281aa7ea21fe4efc497 100644
--- a/tecnicas/controllers/models_controller/ordenes_controller.py
+++ b/tecnicas/controllers/models_controller/ordenes_controller.py
@@ -25,13 +25,13 @@ class OrdenesController():
def saveOrders(self):
if not self.orders:
- return controller_error("no se han establecido las ordenes para guardar")
+ return controller_error("No se han establecido las órdenes para guardar")
try:
for order in self.orders:
order.save()
return self.orders
except DatabaseError as error:
- return controller_error("error al guardar las ordenes")
+ return controller_error("Error al guardar las ordenes")
def setPositions(self):
codes_ids_products = {}
@@ -40,21 +40,21 @@ class OrdenesController():
codes_expect = list(codes_ids_products.keys())
if len(self.orders) != len(self.raw_list_orders):
- return controller_error("el numero de ordenes guardados no coinciden con los recibidos")
+ return controller_error("El número de ordenes guardados no coinciden con los recibidos")
self.positions = []
for index, order in enumerate(self.raw_list_orders):
received_codes_order = list(order.keys())
if set(received_codes_order) != set(codes_expect):
- return controller_error("las ordenes mandadas no contienen los productos esperados")
+ return controller_error("Las ordenes mandadas no contienen los productos esperados")
for name, position_index in order.items():
list_product_use = [product for product in self.products
if product.codigoProducto == name]
if len(list_product_use) != 1:
- return controller_error("no pueden existir dos productos que ocupen la misma posicion de un orden")
+ return controller_error("No pueden existir dos productos que ocupen la misma posición de un orden")
product_use = list_product_use[0]
new_position = Posicion(
@@ -67,13 +67,13 @@ class OrdenesController():
def savePositions(self):
if not self.positions:
- return controller_error("no se han establecido posiciones para guargar")
+ return controller_error("No se han establecido posiciones para guarguar")
try:
for position in self.positions:
position.save()
return self.positions
except DatabaseError as error:
- return controller_error("error al guardar las posiciones")
+ return controller_error("Error al guardar las posiciones")
@staticmethod
def getOrderById(id: int):
diff --git a/tecnicas/controllers/models_controller/palabras_controller.py b/tecnicas/controllers/models_controller/palabras_controller.py
index 89889899df73da846d9a00daf02def02eef4471b..3aafd619ecd57fe10ca3746a4b49257f2ba3bd92 100644
--- a/tecnicas/controllers/models_controller/palabras_controller.py
+++ b/tecnicas/controllers/models_controller/palabras_controller.py
@@ -14,11 +14,9 @@ class PalabrasController():
self.ids_words = new_ids
def setWords(self):
- self.words = []
- searched_words = list(Palabra.objects.filter(id__in=self.ids_words))
- if not len(searched_words):
- return controller_error("no se han encontrado registros")
- self.words = searched_words
+ self.words = list(Palabra.objects.filter(id__in=self.ids_words))
+ if not len(self.words):
+ return controller_error("No se han encontrado registros")
return self.words
@staticmethod
diff --git a/tecnicas/controllers/models_controller/particiapacion_controller.py b/tecnicas/controllers/models_controller/particiapacion_controller.py
index a0149c3d44730ca760130129f93948503e3871b1..bd0448b1ee796fe063fbe69d36ffaed8b2df65a9 100644
--- a/tecnicas/controllers/models_controller/particiapacion_controller.py
+++ b/tecnicas/controllers/models_controller/particiapacion_controller.py
@@ -1,12 +1,13 @@
-from ...models import Participacion, Tecnica, SesionSensorial
+from ...models import Participacion, Tecnica, SesionSensorial, Catador
from ...utils import controller_error
class ParticipacionController():
@staticmethod
- def enterSession(id_participation: int):
+ def enterSession(tester: Catador, session: SesionSensorial):
try:
- participation = Participacion.objects.get(id=id_participation)
+ participation = Participacion.objects.get(
+ catador=tester, tecnica=session.tecnica)
participation.finalizado = False
participation.activo = True
participation.save()
@@ -26,9 +27,10 @@ class ParticipacionController():
return controller_error("No se ha encontrado la participación")
@staticmethod
- def outSession(id_participation: int):
+ def outSession(tester: Catador, session: SesionSensorial):
try:
- participation = Participacion.objects.get(id=id_participation)
+ participation = Participacion.objects.get(
+ catador=tester, tecnica=session.tecnica)
participation.activo = False
participation.save()
return participation
diff --git a/tecnicas/controllers/models_controller/sesion_controller.py b/tecnicas/controllers/models_controller/sesion_controller.py
index e43b8bee3b35be005a1c56e12ab756c3ed6101fb..0ff4c82a05ca7d5421d5e24ac3788d2b14fefc8e 100644
--- a/tecnicas/controllers/models_controller/sesion_controller.py
+++ b/tecnicas/controllers/models_controller/sesion_controller.py
@@ -18,9 +18,9 @@ class SesionController():
def setSession(self):
if not self.presenter:
- return controller_error("se requiere presentador para crear sesion")
+ return controller_error("Se requiere presentador para crear sesión")
elif not self.technique:
- return controller_error("se requiere tecnica para crear sesion")
+ return controller_error("Se requiere técnica para crear sesión")
self.sensorial_session = SesionSensorial(
tecnica=self.technique,
@@ -34,22 +34,22 @@ class SesionController():
def saveSession(self):
if not self.sensorial_session:
- return controller_error("no se ha definido la sesion a guardar")
+ return controller_error("No se ha definido la sesión a guardar")
try:
self.sensorial_session.save()
return self.sensorial_session
except DatabaseError as error:
- return controller_error("Error al crear la session sensorial")
+ return controller_error("Error al crear la sesión sensorial")
@staticmethod
def getSessionsSavesByCretor(user_name: str, page: int):
elements_by_page = 6
try:
- creator = Presentador.objects.get(nombre_usuario=user_name)
+ creator = Presentador.objects.get(user__username=user_name)
except Presentador.DoesNotExist:
- return controller_error("presentador invalido")
+ return controller_error("Presentador invalido")
queryset = (
SesionSensorial.objects
@@ -75,10 +75,14 @@ class SesionController():
sessions_in_page = paginator.page(page)
except PageNotAnInteger:
return controller_error("índice inválido")
- except EmptyPage:
- return controller_error("sin registros de sesiones")
- return (sessions_in_page, not sessions_in_page.number < paginator.num_pages)
+ if not sessions_in_page.object_list:
+ return controller_error("Sin registros de sesiones")
+
+ current_page = sessions_in_page.number
+ is_last_page = not current_page < paginator.num_pages
+
+ return (sessions_in_page, is_last_page, current_page)
@staticmethod
def getSessionByCodePanelTester(code: str):
@@ -111,14 +115,14 @@ class SesionController():
@staticmethod
def getNumberSessionsByCreator(user_name: str):
try:
- creator = Presentador.objects.get(nombre_usuario=user_name)
+ creator = Presentador.objects.get(user__username=user_name)
number_sessions = SesionSensorial.objects.filter(
creadoPor=creator).count()
return number_sessions/9
except Presentador.DoesNotExist:
- return controller_error("presentador invalido")
+ return controller_error("Presentador invalido")
@staticmethod
def finishRepetion(session: SesionSensorial | str):
diff --git a/tecnicas/controllers/models_controller/tecnica_controller.py b/tecnicas/controllers/models_controller/tecnica_controller.py
index 6a155f4b10add6a7b4bfa01b304fbb8cb9f8ad93..71265258841e5c3bcbc1843d6438bfa749275262 100644
--- a/tecnicas/controllers/models_controller/tecnica_controller.py
+++ b/tecnicas/controllers/models_controller/tecnica_controller.py
@@ -6,18 +6,18 @@ from tecnicas.utils import controller_error
class TecnicaController():
def setTechnique(self, **kwargs):
self.technique = Tecnica(
- tipo_tecnica=kwargs["tipo_tecnica"],
- id_estilo=kwargs["estilo_palabras"],
- repeticiones_max=kwargs["numero_repeticiones"] or 0,
+ tipo_tecnica=TipoTecnica.objects.get(nombre_tecnica=kwargs["name_tecnica"]),
+ id_estilo=EstiloPalabra.objects.get(id=kwargs["estilo_palabras"]),
+ repeticiones_max=kwargs["numero_repeticiones"] or 1,
limite_catadores=kwargs["numero_catadores"],
instrucciones=kwargs["instrucciones"],
)
def setTechniqueFromBasicData(self, basic):
self.technique = Tecnica(
- tipo_tecnica=TipoTecnica.objects.get(id=basic["id_tecnica"]),
+ tipo_tecnica=TipoTecnica.objects.get(nombre_tecnica=basic["name_tecnica"]),
id_estilo=EstiloPalabra.objects.get(id=basic["estilo_palabras"]),
- repeticiones_max=basic["numero_repeticiones"] or 0,
+ repeticiones_max=basic["numero_repeticiones"] or 1,
limite_catadores=basic["numero_catadores"],
instrucciones=basic["instrucciones"] or "Espere instrucciones del Presentador",
)
@@ -30,7 +30,7 @@ class TecnicaController():
self.technique.save()
return self.technique
except DatabaseError:
- return controller_error("No se ha podido guardar la tecnica")
+ return controller_error("No se ha podido guardar la técnica")
def deleteTechnique(self):
self.technique.delete()
diff --git a/tecnicas/controllers/views_controller/create_session/panel_basic_controller.py b/tecnicas/controllers/views_controller/create_session/panel_basic_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..7afd63508db47c0be43062860ac2fd9cfcec08da
--- /dev/null
+++ b/tecnicas/controllers/views_controller/create_session/panel_basic_controller.py
@@ -0,0 +1,105 @@
+from tecnicas.forms import SesionBasicForm
+from django.http import HttpRequest
+from django.shortcuts import redirect, render
+from django.urls import reverse
+
+
+class PanelBasicController():
+ conf_initial_rata = {
+ "numero_catadores": 0,
+ "numero_repeticiones": 1
+ }
+
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def controllGetEscalas(request: HttpRequest):
+ form_sesion = SesionBasicForm()
+
+ view_context = {
+ "form_sesion": form_sesion,
+ "use_technique": "escalas"
+ }
+
+ response = render(
+ request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
+ return response
+
+ @staticmethod
+ def controllPostEscalas(request: HttpRequest, name_tecnica: str):
+ try:
+ form = SesionBasicForm(request.POST)
+
+ if form.is_valid():
+ values = {}
+ for name, value in form.cleaned_data.items():
+ if name == "estilo_palabras" or name == "tipo_escala":
+ values[name] = value.id
+ else:
+ values[name] = value
+
+ values["name_tecnica"] = name_tecnica
+ request.session['form_basic'] = values
+ response = redirect(
+ reverse("cata_system:panel_configuracion_tags"))
+ else:
+ response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
+ "form_sesion": form, "error": "Información no valida"})
+ except KeyError:
+ response = redirect(reverse(
+ "cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
+
+ return response
+
+ @staticmethod
+ def controllGetRATA(request: HttpRequest):
+ form_sesion = SesionBasicForm(
+ initial_conf=PanelBasicController.conf_initial_rata)
+
+ view_context = {
+ "form_sesion": form_sesion,
+ "use_technique": "rata"
+ }
+
+ response = render(
+ request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
+ return response
+
+ @staticmethod
+ def controllPostRATA(request: HttpRequest, name_tecnica: str):
+ try:
+ form = SesionBasicForm(
+ request.POST, initial_conf=PanelBasicController.conf_initial_rata)
+
+ if form.is_valid():
+ values = {}
+ for name, value in form.cleaned_data.items():
+ if name == "estilo_palabras" or name == "tipo_escala":
+ values[name] = value.id
+ else:
+ values[name] = value
+
+ for key, expected in PanelBasicController.conf_initial_rata.items():
+ actual = values.get(key)
+
+ if actual is None or str(actual) != str(expected):
+ form.add_error(
+ key, f"Valor inválido para '{key}': se esperaba {expected}, se recibió {actual}")
+
+ if form.errors:
+ response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
+ "form_sesion": form, "error": "No puedes modificar el número de catadores o repeticiones"})
+ else:
+ values["name_tecnica"] = name_tecnica
+ request.session['form_basic'] = values
+ response = redirect(
+ reverse("cata_system:panel_configuracion_tags"))
+ else:
+ response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
+ "form_sesion": form, "error": "Información no valida"})
+ except KeyError:
+ response = redirect(reverse(
+ "cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
+
+ return response
diff --git a/tecnicas/controllers/views_controller/create_session/panel_codes_controller.py b/tecnicas/controllers/views_controller/create_session/panel_codes_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..9bef632d61abfa2d6e72519ddb24780b2d3f430e
--- /dev/null
+++ b/tecnicas/controllers/views_controller/create_session/panel_codes_controller.py
@@ -0,0 +1,114 @@
+from django.http import HttpRequest
+from django.shortcuts import redirect, render
+from django.urls import reverse
+from tecnicas.forms import CodesForm
+from tecnicas.utils import generarCodigos
+import json
+
+
+class PanelCodesController():
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def controllGetEscalas(request: HttpRequest, data):
+ (
+ num_products,
+ num_tester
+ ) = PanelCodesController.defineInfoConvencional(data)
+
+ codes_products = generarCodigos(num_products)
+
+ form_codes = CodesForm(codes=codes_products)
+
+ context_codes_form = {
+ "form_codes": form_codes,
+ "num_tester": num_tester,
+ "use_technique": "escalas"
+ }
+
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+
+ @staticmethod
+ def controllPostEscalas(request: HttpRequest, data):
+ (
+ num_products,
+ num_tester
+ ) = PanelCodesController.defineInfoConvencional(data)
+
+ sorts_code = json.loads(request.POST.get("sort_codes"))
+ codes = []
+ context_codes_form = {}
+
+ for name, value in request.POST.items():
+ if name.__contains__("producto_"):
+ codes.append(value)
+
+ form_codes = CodesForm(request.POST, codes=codes)
+
+ context_codes_form = {
+ "form_codes": form_codes,
+ "num_tester": num_tester,
+ "use_technique": "escalas"
+ }
+
+ if form_codes.is_valid():
+ codes_sort = {"product_codes": []}
+
+ for name, value in form_codes.cleaned_data.items():
+ codes_sort["product_codes"].append({name: value})
+
+ codes_sort["sort_codes"] = sorts_code
+ request.session["form_codes"] = codes_sort
+ return redirect(reverse("cata_system:panel_configuracion_words"))
+ else:
+ context_codes_form["error"] = "error en los datos recibidos"
+
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+
+ @staticmethod
+ def controllGetRATA(request: HttpRequest, data):
+ num_products = data["numero_productos"]
+ codes_products = generarCodigos(num_products)
+ form_codes = CodesForm(codes=codes_products)
+
+ context_codes_form = {
+ "form_codes": form_codes,
+ "num_tester": 0,
+ "use_technique": "rata"
+ }
+
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+
+ @staticmethod
+ def controllPostRATA(request: HttpRequest):
+ codes = []
+ context_codes_form = {}
+
+ for name, value in request.POST.items():
+ if name.__contains__("producto_"):
+ codes.append(value)
+
+ form_codes = CodesForm(request.POST, codes=codes)
+
+ context_codes_form = {
+ "form_codes": form_codes,
+ "use_technique": "rata"
+ }
+
+ if form_codes.is_valid():
+ request.session["form_codes"] = codes
+ return redirect(reverse("cata_system:panel_configuracion_words"))
+ else:
+ context_codes_form["error"] = "error en los datos recibidos"
+
+ return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+
+ @staticmethod
+ def defineInfoConvencional(data):
+ num_products = data["numero_productos"]
+ num_tester = data["numero_catadores"]
+ return (
+ num_products,
+ num_tester
+ )
diff --git a/tecnicas/controllers/views_controller/create_session/panel_create_controller.py b/tecnicas/controllers/views_controller/create_session/panel_create_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..42a3b79c3431966cf333137549af310f92dab72e
--- /dev/null
+++ b/tecnicas/controllers/views_controller/create_session/panel_create_controller.py
@@ -0,0 +1,274 @@
+from django.http import HttpRequest, JsonResponse
+from django.db import transaction
+from django.shortcuts import render
+from tecnicas.utils import general_error
+from tecnicas.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, EstiloPalabrasController, PalabrasController, SesionController
+from tecnicas.utils import deleteDataSession
+
+
+class PanelCreateController():
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def controllGetEscalas(request: HttpRequest):
+ return render(
+ request, 'tecnicas/create_sesion/creando_sesion.html')
+
+ @staticmethod
+ def controllPostEscalas(request: HttpRequest):
+ if request.POST.get('action') == 'create_session':
+ if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
+ deleteDataSession(request)
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
+
+ with transaction.atomic():
+ # ////////////////////////////////////////////////////// #
+ #
+ # First step: Create technique and scale with their tags #
+ #
+ # ////////////////////////////////////////////////////// #
+ data_basic = request.session["form_basic"]
+ controllerTechnique = TecnicaController()
+ controllerTechnique.setTechniqueFromBasicData(basic=data_basic)
+ technique = controllerTechnique.saveTechnique()
+ if not technique:
+ return general_error("Error al guardar la técnica")
+
+ data_scale = {
+ "id_scale": data_basic["tipo_escala"],
+ "size": data_basic["tamano_escala"],
+ "technique": technique
+ }
+
+ controllerScale = EscalaController(data=data_scale)
+
+ scale = controllerScale.saveScale()
+ if isinstance(scale, dict):
+ return general_error(scale["error"])
+
+ dict_tags = request.session["form_tags"]
+ saved_related_tags = controllerScale.realteTags(dict_tags)
+ if "error" in saved_related_tags:
+ return general_error(saved_related_tags["error"])
+
+ # ////////////////////////////////////////////////////////// #
+ #
+ # Second step: Create orders, productos and set the position #
+ #
+ # ////////////////////////////////////////////////////////// #
+ data_codes = request.session["form_codes"]
+
+ list_codes_dict = data_codes["product_codes"]
+
+ codes = []
+ for product in list_codes_dict:
+ code = next(iter(product.values()))
+ codes.append(code)
+
+ controllerProducts = ProductosController(
+ codes=codes,
+ technique=technique
+ )
+
+ controllerProducts.setProductsNoSave()
+ saved_prodcuts = controllerProducts.saveProducts()
+ if isinstance(saved_prodcuts, dict):
+ return general_error(saved_prodcuts["error"])
+
+ raw_sort_codes = data_codes["sort_codes"]
+ controllerOrdes = OrdenesController(
+ raw_orders=raw_sort_codes,
+ list_products=saved_prodcuts,
+ technique=technique
+ )
+
+ controllerOrdes.setOrdersToSave()
+ saved_orders = controllerOrdes.saveOrders()
+ if isinstance(saved_orders, dict):
+ return general_error(saved_orders["error"])
+
+ seded_positions = controllerOrdes.setPositions()
+ if isinstance(seded_positions, dict):
+ return general_error(seded_positions["error"])
+
+ saved_postions = controllerOrdes.savePositions()
+ if isinstance(saved_postions, dict):
+ return general_error(saved_prodcuts["error"])
+
+ # /////////////////////////////////////////////////////// #
+ #
+ # Third step: Create relations technique with Words Style #
+ #
+ # /////////////////////////////////////////////////////// #
+ ids_words = request.session["form_words"]
+ words_controller = PalabrasController(ids=ids_words)
+
+ words_to_use = words_controller.setWords()
+ if isinstance(words_to_use, dict):
+ return general_error(words_to_use["error"])
+
+ style_controller = EstiloPalabrasController(
+ technique=technique, words=words_to_use)
+
+ instace_style = style_controller.createAndSaveInstaceStyle()
+ if isinstance(instace_style, dict):
+ return general_error(instace_style["error"])
+
+ words_using = style_controller.relatedWords()
+ if isinstance(words_using, dict):
+ return general_error(words_using["error"])
+
+ # //////////////////////////////////////////////////////// #
+ #
+ # Fourth step: Create session and relat with the technique #
+ #
+ # //////////////////////////////////////////////////////// #
+ session_controller = SesionController(
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
+ technique=technique,
+ creator=request.user.user_presentador
+ )
+
+ setting_session = session_controller.setSession()
+ if isinstance(setting_session, dict):
+ return general_error(setting_session["error"])
+
+ saved_session = session_controller.saveSession()
+ if isinstance(saved_session, dict):
+ return general_error(saved_session["error"])
+
+ context = {
+ "message": "sesión creada",
+ "data": {
+ "codigo_sesion": saved_session.codigo_sesion,
+ "nombre_sesion": saved_session.nombre_sesion
+ }
+ }
+
+ # ////////////////////////////////// #
+ #
+ # Final step: Delete date in session #
+ #
+ # ////////////////////////////////// #
+
+ deleteDataSession(request)
+ return JsonResponse(context)
+ else:
+ return general_error("No se ha establecido acción")
+
+ @staticmethod
+ def controllPostRATA(request: HttpRequest):
+ if request.POST.get('action') == 'create_session':
+ if not request.session.get("form_tags") or not request.session.get("form_codes") or not request.session.get("form_words"):
+ deleteDataSession(request)
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
+
+ with transaction.atomic():
+ # ////////////////////////////////////////////////////// #
+ #
+ # First step: Create technique and scale with their tags #
+ #
+ # ////////////////////////////////////////////////////// #
+ data_basic = request.session["form_basic"]
+ data_basic["numero_catadores"] = 0
+ data_basic["numero_repeticiones"] = 1
+ controllerTechnique = TecnicaController()
+ controllerTechnique.setTechniqueFromBasicData(basic=data_basic)
+ technique = controllerTechnique.saveTechnique()
+ if not technique:
+ return general_error("Error al guardar la técnica")
+
+ data_scale = {
+ "id_scale": data_basic["tipo_escala"],
+ "size": data_basic["tamano_escala"],
+ "technique": technique
+ }
+
+ controllerScale = EscalaController(data=data_scale)
+
+ scale = controllerScale.saveScale()
+ if isinstance(scale, dict):
+ return general_error(scale["error"])
+
+ dict_tags = request.session["form_tags"]
+ saved_related_tags = controllerScale.realteTags(dict_tags)
+ if "error" in saved_related_tags:
+ return general_error(saved_related_tags["error"])
+
+ # ////////////////////////////////////////////// #
+ #
+ # Second step: Create productos with their codes #
+ #
+ # ////////////////////////////////////////////// #
+ codes = request.session["form_codes"]
+
+ controllerProducts = ProductosController(
+ codes=codes,
+ technique=technique
+ )
+
+ controllerProducts.setProductsNoSave()
+ saved_prodcuts = controllerProducts.saveProducts()
+ if isinstance(saved_prodcuts, dict):
+ return general_error(saved_prodcuts["error"])
+
+ # /////////////////////////////////////////////////////// #
+ #
+ # Third step: Create relations technique with Words Style #
+ #
+ # /////////////////////////////////////////////////////// #
+ ids_words = request.session["form_words"]
+ words_controller = PalabrasController(ids=ids_words)
+
+ words_to_use = words_controller.setWords()
+ if isinstance(words_to_use, dict):
+ return general_error(words_to_use["error"])
+
+ style_controller = EstiloPalabrasController(
+ technique=technique, words=words_to_use)
+
+ instace_style = style_controller.createAndSaveInstaceStyle()
+ if isinstance(instace_style, dict):
+ return general_error(instace_style["error"])
+
+ words_using = style_controller.relatedWords()
+ if isinstance(words_using, dict):
+ return general_error(words_using["error"])
+
+ # //////////////////////////////////////////////////////// #
+ #
+ # Fourth step: Create session and relat with the technique #
+ #
+ # //////////////////////////////////////////////////////// #
+ session_controller = SesionController(
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
+ technique=technique,
+ creator=request.user.user_presentador
+ )
+
+ setting_session = session_controller.setSession()
+ if isinstance(setting_session, dict):
+ return general_error(setting_session["error"])
+
+ saved_session = session_controller.saveSession()
+ if isinstance(saved_session, dict):
+ return general_error(saved_session["error"])
+
+ context = {
+ "message": "sesión creada",
+ "data": {
+ "codigo_sesion": saved_session.codigo_sesion,
+ "nombre_sesion": saved_session.nombre_sesion
+ }
+ }
+
+ # ////////////////////////////////// #
+ #
+ # Final step: Delete date en session #
+ #
+ # ////////////////////////////////// #
+ deleteDataSession(request)
+ return JsonResponse(context)
+ else:
+ return general_error("No se ha establecido acción")
diff --git a/tecnicas/controllers/views_controller/create_session/panel_tags_controller.py b/tecnicas/controllers/views_controller/create_session/panel_tags_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..852bd7d245c22894b486e2fd0f5a65e462a8b201
--- /dev/null
+++ b/tecnicas/controllers/views_controller/create_session/panel_tags_controller.py
@@ -0,0 +1,71 @@
+from django.http import HttpRequest
+from django.shortcuts import redirect, render
+from django.urls import reverse
+from tecnicas.forms import SesionTagsForm, EtiquetaForm
+from tecnicas.models import TipoEscala
+
+
+class PanelTagsController():
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def controllGetEscalas(request: HttpRequest, data):
+ (
+ type_scale,
+ tamano_escala,
+ form_new_etiqueta
+ ) = PanelTagsController.defineInfoScale(data)
+
+ form_etiqutas = SesionTagsForm(
+ longitud=tamano_escala, tipo_escala=type_scale.nombre_escala)
+
+ context_tags = {
+ "form_tags": form_etiqutas,
+ "form_new_tag": form_new_etiqueta
+ }
+
+ return render(request, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
+
+ @staticmethod
+ def controllPostEscalas(request: HttpRequest, data):
+ (
+ type_scale,
+ tamano_escala,
+ form_new_etiqueta
+ ) = PanelTagsController.defineInfoScale(data)
+
+ values = {}
+ form = SesionTagsForm(request.POST, longitud=tamano_escala,
+ tipo_escala=type_scale.nombre_escala)
+
+ context_tags = {
+ "form_tags": form,
+ "form_new_tag": form_new_etiqueta
+ }
+
+ if form.is_valid():
+ for name, value in form.cleaned_data.items():
+ values[name] = value.id
+
+ request.session["form_tags"] = values
+ response = redirect(
+ reverse("cata_system:panel_configuracion_codes"))
+ else:
+ context_tags["error"] = "ha ocurrido un error"
+ response = render(
+ request, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
+
+ return response
+
+ @staticmethod
+ def defineInfoScale(data):
+ type_scale = TipoEscala.objects.get(pk=data["tipo_escala"])
+ tamano_escala = data["tamano_escala"]
+ form_new_etiqueta = EtiquetaForm()
+
+ return (
+ type_scale,
+ tamano_escala,
+ form_new_etiqueta
+ )
diff --git a/tecnicas/controllers/views_controller/create_session/panel_words_controller.py b/tecnicas/controllers/views_controller/create_session/panel_words_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..138897bfc993e5de157192468123479cbf5b32a6
--- /dev/null
+++ b/tecnicas/controllers/views_controller/create_session/panel_words_controller.py
@@ -0,0 +1,49 @@
+from django.http import HttpRequest
+from tecnicas.forms import WordForm
+from django.shortcuts import render, redirect
+from django.urls import reverse
+from tecnicas.models import Palabra
+import json
+
+
+class PanelWordsController():
+ def __init__(self):
+ pass
+
+ @staticmethod
+ def controllGetEscalas(request: HttpRequest):
+ form = WordForm()
+ context = {
+ "form_word": form
+ }
+
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
+
+ @staticmethod
+ def controllPostEscalas(request: HttpRequest):
+ form = WordForm()
+ context = {
+ "form_word": form
+ }
+
+ if not request.POST.get("words"):
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
+
+ words = json.loads(request.POST.get("words"))
+ context["words"] = words
+
+ ids_words = [word["id"] for word in words]
+
+ if len(ids_words) != len(set(ids_words)):
+ context["error"] = "existen palabras duplicadas"
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
+
+ exist_words = Palabra.objects.filter(
+ id__in=ids_words).count() == len(ids_words)
+
+ if not exist_words:
+ context["error"] = "algunas palabras no existen"
+ return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
+
+ request.session["form_words"] = ids_words
+ return redirect(reverse("cata_system:creando_sesion"))
diff --git a/tecnicas/controllers/views_controller/detalles_sesion_controller.py b/tecnicas/controllers/views_controller/detalles_sesion_controller.py
deleted file mode 100644
index 5f8e3f91c57d245ed0ef13379fd773f100a9e1f0..0000000000000000000000000000000000000000
--- a/tecnicas/controllers/views_controller/detalles_sesion_controller.py
+++ /dev/null
@@ -1,95 +0,0 @@
-'''
-
-Para Tecnicas Convencionales, CATA, RATA, Escala Hedonica
-Encabezados de como deben de aparecer los datos por repeticion
-
-| Repeticion: R
-| Codigo Producto | Catador | P1 | P2 | P3 | Pn |
-
-Encabezados de como deben de aparecer los datos juntos
-
-| Repeticion | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
-
-'''
-
-from ...models import SesionSensorial, Presentador, Tecnica, Palabra
-from .. import CalificacionController, PalabrasController
-from ...utils import controller_error
-from collections import defaultdict
-from tecnicas.controllers import DatoController
-from tecnicas.utils import defaultdict_to_dict
-
-
-class DetallesSesionController():
- def __init__(self, session_code: str):
- self.session = SesionSensorial.objects.get(codigo_sesion=session_code)
-
- def getContextForView(self):
- self.context = {}
-
- self.context["sesion"] = self.session
-
- self.words = PalabrasController.getWordsInTechnique(
- self.session.tecnica)
- self.context["palabras"] = [word.nombre_palabra for word in self.words]
-
- def getContextWithData(self):
- ratings_for_repetition = []
-
- ratings = CalificacionController.getRatingsByTechnique(
- technique=self.session.tecnica)
-
- if isinstance(ratings, dict) or not ratings:
- self.context["calificaciones"] = ratings_for_repetition
- self.context["existen_calificaciones"] = False
- return self.context
-
- data = DatoController.getWordValuesForConvecional(
- ratings=ratings, technique=self.session.tecnica)
-
- ratings_for_repetition = defaultdict(
- lambda: defaultdict(lambda: defaultdict(list)))
-
- for item in data:
- user = item["usuarioCatador"]
- rep = item["repeticion"]
- prod = item["producto_code"]
-
- ratings_for_repetition[rep][user][prod].append({
- "nombre_palabra": item["nombre_palabra"],
- "dato_valor": item["dato_valor"]
- })
-
- self.context["calificaciones"] = defaultdict_to_dict(
- ratings_for_repetition)
- self.context["existen_calificaciones"] = True
-
- return self.context
-
- @staticmethod
- def startRepetition(session_code: str, username: str):
- try:
- creator = Presentador.objects.get(nombre_usuario=username)
- session = SesionSensorial.objects.get(codigo_sesion=session_code)
- technique = Tecnica.objects.get(id=session.tecnica.id)
- except Presentador.DoesNotExist:
- return controller_error("no existe presentador")
- except SesionSensorial.DoesNotExist:
- return controller_error("no existe sesión sensorial")
- except Tecnica.DoesNotExist:
- return controller_error("Ha ocurrido un error al recuperar la técnica")
-
- if creator.nombre_usuario != session.creadoPor.nombre_usuario:
- return controller_error("solo el presentador que crea la sesión puede iniciar la repetición")
- elif session.activo:
- return controller_error("la sesión ya está activada")
- elif technique.repeticion == technique.repeticiones_max:
- return controller_error("se ha alcanzado el número de repeticiones máxima")
-
- session.activo = True
- technique.repeticion = technique.repeticion + 1
-
- technique.save()
- session.save()
-
- return session
diff --git a/tecnicas/controllers/views_controller/list_sessions_tester_controller.py b/tecnicas/controllers/views_controller/list_sessions_tester_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b775050b5bbe5372fe193308493782f00ca9eb8
--- /dev/null
+++ b/tecnicas/controllers/views_controller/list_sessions_tester_controller.py
@@ -0,0 +1,76 @@
+from django.core.paginator import Paginator, PageNotAnInteger
+from django.db.models import OuterRef, Subquery, BooleanField
+from tecnicas.models import Catador, SesionSensorial, Participacion
+from tecnicas.utils import controller_error
+
+
+class ListSessionsTesterController():
+ def __init__(self):
+ pass
+
+ def getContext(self, user: Catador, page: int):
+ context_view = {}
+
+ response = ListSessionsTesterController.getSessionByTester(page, user)
+ if isinstance(response, dict):
+ return response
+
+ (sessions, is_last_page, current_page) = response
+ context_view["sessions"] = sessions
+ context_view["last_page"] = is_last_page
+ context_view["page"] = current_page
+
+ return context_view
+
+ @staticmethod
+ def getSessionByTester(page: int, tester: Catador):
+ elements_by_page = 6
+
+ base_qs = SesionSensorial.objects.filter(
+ tecnica__tecnica_participacion__catador=tester
+ )
+
+ participacion_finalizado_subq = Subquery(
+ Participacion.objects.filter(
+ tecnica=OuterRef('tecnica_id'),
+ catador=tester
+ ).values('finalizado')[:1],
+ output_field=BooleanField()
+ )
+
+ participacion_activo_subq = Subquery(
+ Participacion.objects.filter(
+ tecnica=OuterRef('tecnica_id'),
+ catador=tester
+ ).values('activo')[:1],
+ output_field=BooleanField()
+ )
+
+ queryset = (
+ base_qs
+ .select_related(
+ 'tecnica',
+ 'tecnica__tipo_tecnica',
+ 'tecnica__id_estilo',
+ )
+ .annotate(
+ participacion_finalizado=participacion_finalizado_subq,
+ participacion_activo=participacion_activo_subq,
+ )
+ .order_by('participacion_finalizado', '-activo', '-fechaCreacion')
+ .distinct()
+ )
+
+ paginator = Paginator(queryset, elements_by_page)
+ try:
+ sessions_in_page = paginator.page(page)
+ except PageNotAnInteger:
+ return controller_error("índice inválido")
+
+ if not sessions_in_page.object_list:
+ return controller_error("Sin registros de Participaciones")
+
+ current_page = sessions_in_page.number
+ is_last_page = not current_page < paginator.num_pages
+
+ return (sessions_in_page, is_last_page, current_page)
diff --git a/tecnicas/controllers/views_controller/login_tester_controller.py b/tecnicas/controllers/views_controller/login_tester_controller.py
index 3ce9c33d9659fdb3fd4758754c2f74e0d7a6e619..1b9c7849593f2f58ae9c3849537783b4b9608bc4 100644
--- a/tecnicas/controllers/views_controller/login_tester_controller.py
+++ b/tecnicas/controllers/views_controller/login_tester_controller.py
@@ -1,12 +1,17 @@
-from ...models import Catador, SesionSensorial, Participacion
-from ...utils import controller_error
+from django.http import HttpRequest, JsonResponse
+from django.shortcuts import render, redirect
+from django.urls import reverse
from django.db import transaction
+from tecnicas.models import Catador, SesionSensorial, Participacion
+from tecnicas.utils import controller_error
class LoginTesterController():
tester: Catador
session: SesionSensorial
taster_participation: Participacion
+ current_direcction = "tecnicas/forms_tester/login_session.html"
+ destinity_direcction = "cata_system:catador_init_session"
def __init__(self):
self.tester = Catador()
@@ -14,7 +19,7 @@ class LoginTesterController():
def existCredential(self, user_tester: str, code_session: str):
try:
- self.tester = Catador.objects.get(usuarioCatador=user_tester)
+ self.tester = Catador.objects.get(user__username=user_tester)
self.session = SesionSensorial.objects.get(
codigo_sesion=code_session)
@@ -22,42 +27,73 @@ class LoginTesterController():
except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
return controller_error("Credenciales inválidas")
- def validateEntry(self):
- if not self.tester.nombre or not self.session.codigo_sesion:
- return controller_error("Credenciales no definidas")
-
+ def validateEntryEscalas(self, request=HttpRequest):
+ context = {}
if not self.session.activo:
- return controller_error("La sesión no está activa actualmente")
+ context["error"] = "La sesión no está activa actualmente"
+ return render(request, self.current_direcction, context)
if self.session.tecnica.repeticion > 1:
try:
self.taster_participation = Participacion.objects.get(
tecnica=self.session.tecnica, catador=self.tester)
- return self.taster_participation
+ context["error"] = "Usted ya esta dentro de la sesión"
+ return render(request, self.current_direcction, context)
except Participacion.DoesNotExist:
- return controller_error("No tienes permitido entrar a esta sesión")
+ context["error"] = "No tienes permitido entrar a esta sesión"
+ return render(request, self.current_direcction, context)
else:
+ with transaction.atomic():
+ code_session = self.session.codigo_sesion
+ self.session = SesionSensorial.objects.select_for_update().get(
+ codigo_sesion=code_session)
+
+ max_testers = self.session.tecnica.limite_catadores
+ current_num_testers = Participacion.objects.filter(
+ tecnica=self.session.tecnica).count()
+
+ if current_num_testers >= max_testers:
+ context["error"] = "La sesión ha alcanzado el número máximo de catadores"
+ return render(request, self.current_direcction, context)
+
+ self.taster_participation = Participacion.objects.create(
+ tecnica=self.session.tecnica,
+ catador=self.tester,
+ finalizado=False
+ )
+ params = {
+ "code_sesion": self.session.codigo_sesion
+ }
+ return redirect(reverse(self.destinity_direcction, kwargs=params))
+
+ def validateEntryRATA(self, request: HttpRequest):
+ context = {}
+ if not self.session.activo:
+ context["error"] = "La sesión no está activa actualmente"
+ return render(request, self.current_direcction, context)
+
+ if self.session.tecnica.repeticion <= 1:
try:
self.taster_participation = Participacion.objects.get(
tecnica=self.session.tecnica, catador=self.tester)
- return self.taster_participation
+ context["error"] = "Usted ya esta dentro de la sesión"
+ return render(request, self.current_direcction, context)
except Participacion.DoesNotExist:
with transaction.atomic():
code_session = self.session.codigo_sesion
self.session = SesionSensorial.objects.select_for_update().get(
codigo_sesion=code_session)
- max_testers = self.session.tecnica.limite_catadores
- current_num_testers = Participacion.objects.filter(
- tecnica=self.session.tecnica).count()
-
- if current_num_testers >= max_testers:
- return controller_error("La sesión ha alcanzado el número máximo de catadores")
-
self.taster_participation = Participacion.objects.create(
tecnica=self.session.tecnica,
catador=self.tester,
finalizado=False
)
- return self.taster_participation
+ params = {
+ "code_sesion": self.session.codigo_sesion
+ }
+ return redirect(reverse(self.destinity_direcction, kwargs=params))
+ else:
+ context["error"] = "Imposible acceder a esta sesión"
+ return render(request, self.current_direcction, context)
diff --git a/tecnicas/controllers/views_controller/main_tester_form_controller.py b/tecnicas/controllers/views_controller/main_tester_form_controller.py
index eb58535fa02bd7ae97b8cafd55d90715b5f0d471..f35d2fc57bed07f5d880476303944552fa59587d 100644
--- a/tecnicas/controllers/views_controller/main_tester_form_controller.py
+++ b/tecnicas/controllers/views_controller/main_tester_form_controller.py
@@ -10,7 +10,7 @@ class MainTesterFormController():
def __init__(self, code_session: str, user_tester: str):
try:
- self.tester = Catador.objects.get(usuarioCatador=user_tester)
+ self.tester = Catador.objects.get(user__username=user_tester)
self.session = SesionSensorial.objects.get(
codigo_sesion=code_session)
except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
@@ -21,6 +21,8 @@ class MainTesterFormController():
orders_without_tester = list(Orden.objects.select_for_update().filter(
id_tecnica=self.session.tecnica, id_catador=None))
+ print(orders_without_tester)
+
if not orders_without_tester:
return controller_error("Las ordenes se han acabado")
@@ -32,21 +34,20 @@ class MainTesterFormController():
return self.order_to_assign
- def checkAssignOrder(self):
- if not self.tester or not self.session:
- return controller_error("Atributos no establecidos")
-
+ def checkAndAssignOrder(self):
try:
- res_order = Orden.objects.get(
+ self.order_to_assign = Orden.objects.get(
id_tecnica=self.session.tecnica, id_catador=self.tester)
- self.order = res_order
- return self.order
except Orden.DoesNotExist:
- return controller_error("Catador sin orden")
+ create = self.assignOrder()
+ if isinstance(create, dict):
+ return create
+ return self.order_to_assign
- def isEndedSession(self, id_participation: int, repetition: int):
+ def isEndedSession(self, repetition: int):
try:
- participation = Participacion.objects.get(id=id_participation)
+ participation = Participacion.objects.get(
+ catador=self.tester, tecnica=self.session.tecnica)
# ////////////////////////////////////////////////////////////// #
#
@@ -76,7 +77,7 @@ class MainTesterFormController():
expected_ratings_repetition = num_products * num_words
- return num_ratings_now >= expected_ratings_repetition
+ return num_ratings_now >= expected_ratings_repetition
else:
return participation.finalizado
except Participacion.DoesNotExist:
diff --git a/tecnicas/controllers/views_controller/monitor_sesion_controller.py b/tecnicas/controllers/views_controller/monitor_sesion_controller.py
deleted file mode 100644
index 36545334984f2abf204b1550d86b92926738c62f..0000000000000000000000000000000000000000
--- a/tecnicas/controllers/views_controller/monitor_sesion_controller.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from tecnicas.models import SesionSensorial, EsAtributo, EsVocabulario, Producto, Calificacion
-from tecnicas.controllers import ParticipacionController, SesionController
-from tecnicas.utils import controller_error
-
-
-class MonitorSesionController():
- def __init__(self, session_code: str):
- self.code_session = session_code
-
- def defineSession(self):
- self.session = SesionSensorial.objects.get(
- codigo_sesion=self.code_session)
-
- def monitorView(self):
- try:
- self.sensorial_session = SesionSensorial.objects.select_related(
- "tecnica"
- ).only(
- "nombre_sesion",
- "tecnica__limite_catadores",
- ).get(codigo_sesion=self.code_session)
-
- self.participations = ParticipacionController.getParticipationsInTechinique(
- self.sensorial_session.tecnica)
- except SesionSensorial.DoesNotExist as error:
- return controller_error("No existe Sesión sensorial")
-
- context = {
- "session_name": self.sensorial_session.nombre_sesion,
- "max_testers": self.sensorial_session.tecnica.limite_catadores,
- "current_testers": len(self.participations),
- "active_testers": len([part for part in self.participations if part.activo]),
- "participations": self.participations
- }
-
- return context
-
- def getExpectedRatings(self):
- num_products = Producto.objects.filter(
- id_tecnica=self.session.tecnica).count()
-
- style_words = self.session.tecnica.id_estilo
-
- num_words: int
-
- if style_words.nombre_estilo == "atributos":
- num_words = EsAtributo.objects.get(
- id_tecnica=self.session.tecnica).palabras.count()
- elif style_words.nombre_estilo == "vocabulario":
- num_words = EsVocabulario.objects.get(
- id_tecnica=self.session.tecnica).id_vocabulario.palabras.count()
-
- return num_products * num_words
-
- def checkAllParticipantsEnded(self):
- self.defineSession()
-
- technique = self.session.tecnica
-
- expected_ratings_repetition = self.getExpectedRatings()
-
- all_participations = ParticipacionController.getParticipationsInTechinique(
- technique=technique)
-
- if len(all_participations) < technique.limite_catadores:
- return (False, "No se ha alcanzado el número máximo de Catadores")
-
- for particiapation in all_participations:
- num_ratings_now = Calificacion.objects.filter(
- id_tecnica=technique, id_catador=particiapation.catador, num_repeticion=technique.repeticion).count()
-
- if num_ratings_now < expected_ratings_repetition:
- return (False, "No todos los catadores han finalizado su evaluación")
-
- return (True, "Puedes finalizar la sesión")
-
- def finishSession(self):
- response = SesionController.finishRepetion(self.session)
- if isinstance(response, dict):
- return controller_error(response["error"])
- self.defineSession()
- return self.session
diff --git a/tecnicas/controllers/views_controller/session_management/details_controller.py b/tecnicas/controllers/views_controller/session_management/details_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..b98cfe897ed711c8084f45eb50e152f5641fc229
--- /dev/null
+++ b/tecnicas/controllers/views_controller/session_management/details_controller.py
@@ -0,0 +1,13 @@
+from tecnicas.models import SesionSensorial, Presentador, Tecnica
+from tecnicas.utils import controller_error
+
+
+class DetallesController():
+ url_template = "tecnicas/manage_sesions/detalles-sesion.html"
+
+ def __init__(self, session: SesionSensorial):
+ self.session = session
+
+ def deleteSesorialSession(self):
+ technique = Tecnica.objects.get(id=self.session.tecnica.id)
+ technique.delete()
\ No newline at end of file
diff --git a/tecnicas/controllers/views_controller/session_management/details_escala_controller.py b/tecnicas/controllers/views_controller/session_management/details_escala_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..9580e3cd41c7058944b9925611789aa0f6255c5e
--- /dev/null
+++ b/tecnicas/controllers/views_controller/session_management/details_escala_controller.py
@@ -0,0 +1,103 @@
+'''
+
+Para Tecnicas Convencionales, CATA, RATA, Escala Hedonica
+Encabezados de como deben de aparecer los datos por repeticion
+
+| Repeticion: R
+| Codigo Producto | Catador | P1 | P2 | P3 | Pn |
+
+Encabezados de como deben de aparecer los datos juntos
+
+| Repeticion | Codigo Producto | Catador | P1 | P2 | P3 | Pn |
+
+'''
+from django.http import HttpRequest
+from django.shortcuts import render, redirect
+from django.urls import reverse
+from tecnicas.models import SesionSensorial, Presentador, Tecnica
+from tecnicas.controllers import DatoController, CalificacionController, PalabrasController
+from .details_controller import DetallesController
+from tecnicas.utils import defaultdict_to_dict, controller_error
+from collections import defaultdict
+
+
+class DetallesEscalasController(DetallesController):
+ url_template = "tecnicas/manage_sesions/detalles-sesion.html"
+
+ def __init__(self, session: SesionSensorial):
+ super().__init__(session)
+
+ def getResponse(self, request: HttpRequest, error: str = "", message: str = ""):
+ context = self.getContext()
+
+ if error != "" or error:
+ context["error"] = error
+ if message != "" or message:
+ context["message"] = message
+
+ return render(
+ request, self.url_template, context)
+
+ def getContext(self):
+ self.context = {
+ "use_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
+ }
+ self.context["sesion"] = self.session
+ self.words = PalabrasController.getWordsInTechnique(
+ self.session.tecnica)
+ self.context["palabras"] = [word.nombre_palabra for word in self.words]
+
+ ratings_for_repetition = []
+
+ ratings = CalificacionController.getRatingsByTechnique(
+ technique=self.session.tecnica)
+
+ if isinstance(ratings, dict) or not ratings:
+ self.context["calificaciones"] = ratings_for_repetition
+ self.context["existen_calificaciones"] = False
+ return self.context
+
+ data = DatoController.getWordValuesForConvecional(
+ ratings=ratings, technique=self.session.tecnica)
+
+ ratings_for_repetition = defaultdict(
+ lambda: defaultdict(lambda: defaultdict(list)))
+
+ for item in data:
+ user = item["usuarioCatador"]
+ rep = item["repeticion"]
+ prod = item["producto_code"]
+
+ ratings_for_repetition[rep][user][prod].append({
+ "nombre_palabra": item["nombre_palabra"],
+ "dato_valor": item["dato_valor"]
+ })
+
+ self.context["calificaciones"] = defaultdict_to_dict(
+ ratings_for_repetition)
+ self.context["existen_calificaciones"] = True
+
+ return self.context
+
+ def startRepetition(self, presenter: Presentador):
+ creator = presenter
+ technique = self.session.tecnica
+
+ if creator.user.username != self.session.creadoPor.user.username:
+ return self.getResponse(error="Solo el presentador que crea la sesión puede iniciar la repetición")
+ elif self.session.activo:
+ return self.getResponse(error="La sesión ya está activada")
+ elif technique.repeticion == technique.repeticiones_max:
+ return self.getResponse(error="Se ha alcanzado el número de repeticiones máxima")
+
+ self.session.activo = True
+ technique.repeticion = technique.repeticion + 1
+
+ technique.save()
+ self.session.save()
+
+ parameters = {
+ "session_code": self.session.codigo_sesion
+ }
+ return redirect(
+ reverse("cata_system:monitor_sesion", kwargs=parameters))
diff --git a/tecnicas/controllers/views_controller/session_management/details_rata_controller.py b/tecnicas/controllers/views_controller/session_management/details_rata_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0b437791c57de37b9354b41e7d18bdd33417635
--- /dev/null
+++ b/tecnicas/controllers/views_controller/session_management/details_rata_controller.py
@@ -0,0 +1,56 @@
+from django.http import HttpRequest
+from django.shortcuts import render, redirect
+from tecnicas.models import SesionSensorial
+from tecnicas.controllers import PalabrasController, DatoController, CalificacionController
+from tecnicas.utils import defaultdict_to_dict
+from .details_controller import DetallesController
+from collections import defaultdict
+
+
+class DetallesRATAController(DetallesController):
+ def __init__(self, session: SesionSensorial):
+ super().__init__(session)
+
+ def getResponse(self, request: HttpRequest, error: str = ""):
+ context = self.getContext()
+ if error != "" or error:
+ context["error"] = error
+ return render(
+ request, self.url_template, context)
+
+ def getContext(self):
+ self.context = {}
+ self.context["sesion"] = self.session
+ self.words = PalabrasController.getWordsInTechnique(
+ self.session.tecnica)
+ self.context["palabras"] = [word.nombre_palabra for word in self.words]
+
+ ratings_for_repetition = []
+
+ ratings = CalificacionController.getRatingsByTechnique(
+ technique=self.session.tecnica)
+
+ if isinstance(ratings, dict) or not ratings:
+ self.context["calificaciones"] = ratings_for_repetition
+ self.context["existen_calificaciones"] = False
+ return self.context
+
+ data = DatoController.getWordValuesForConvecional(
+ ratings=ratings, technique=self.session.tecnica)
+
+ ratings_for_repetition = defaultdict(
+ lambda: defaultdict(lambda: defaultdict(list)))
+
+ for item in data:
+ user = item["usuarioCatador"]
+ rep = item["repeticion"]
+ prod = item["producto_code"]
+
+ ratings_for_repetition[rep][user][prod].append({
+ "nombre_palabra": item["nombre_palabra"],
+ "dato_valor": item["dato_valor"]
+ })
+
+ self.context["calificaciones"] = defaultdict_to_dict(
+ ratings_for_repetition)
+ self.context["existen_calificaciones"] = True
diff --git a/tecnicas/controllers/views_controller/session_management/monitor_controller.py b/tecnicas/controllers/views_controller/session_management/monitor_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..cf0dacad71a06db7a9c1d94deef1e9c7b79ec5fd
--- /dev/null
+++ b/tecnicas/controllers/views_controller/session_management/monitor_controller.py
@@ -0,0 +1,10 @@
+from tecnicas.models import SesionSensorial
+
+
+class MonitorController():
+ def __init__(self, session: SesionSensorial):
+ self.sensorial_session = session
+
+ def updataSession(self):
+ self.sensorial_session = SesionSensorial.objects.get(
+ codigo_sesion=self.sensorial_session.codigo_sesion)
diff --git a/tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py b/tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..eeb47150440a00bc262f7c7e4cb5b4b1414cacf6
--- /dev/null
+++ b/tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py
@@ -0,0 +1,93 @@
+from django.http import HttpRequest
+from django.shortcuts import render, redirect
+from django.urls import reverse
+from tecnicas.models import SesionSensorial, EsAtributo, EsVocabulario, Producto, Calificacion
+from tecnicas.controllers import ParticipacionController, SesionController
+from tecnicas.utils import controller_error
+from .monitor_controller import MonitorController
+
+
+class MonitorEscalasController(MonitorController):
+ url_view = "tecnicas/manage_sesions/monitor-sesion.html"
+
+ def __init__(self, session: SesionController):
+ super().__init__(session)
+
+ def controlGetResponse(self, request: HttpRequest, error: str = "", message: str = ""):
+ self.setContext()
+
+ if error != "" or error:
+ self.context["error"] = error
+ if message != "" or message:
+ self.context["message"] = message
+
+ return render(request, self.url_view, self.context)
+
+ def controlPostResponseFinishSession(self, request: HttpRequest):
+ self.setContext()
+ (is_all_end, message) = self.checkAllParticipantsEnded()
+ if not is_all_end:
+ self.context["error"] = message
+ return render(request, "tecnicas/manage_sesions/monitor-sesion.html", self.context)
+ response = self.finishSession()
+ if isinstance(response, dict):
+ self.context["error"] = response["error"]
+ return render(request, "tecnicas/manage_sesions/monitor-sesion.html", self.context)
+ self.context["message"] = message
+ return redirect(reverse("cata_system:detalles_sesion", kwargs={"session_code": self.sensorial_session.codigo_sesion}))
+
+ def setContext(self):
+ self.participations = ParticipacionController.getParticipationsInTechinique(
+ self.sensorial_session.tecnica)
+
+ self.context = {
+ "code_session": self.sensorial_session.codigo_sesion,
+ "session_name": self.sensorial_session.nombre_sesion,
+ "max_testers": self.sensorial_session.tecnica.limite_catadores,
+ "current_testers": len(self.participations),
+ "active_testers": len([part for part in self.participations if part.activo]),
+ "participations": self.participations,
+ "use_technique": self.sensorial_session.tecnica.tipo_tecnica.nombre_tecnica
+ }
+
+ def getExpectedRatings(self):
+ num_products = Producto.objects.filter(
+ id_tecnica=self.sensorial_session.tecnica).count()
+ style_words = self.sensorial_session.tecnica.id_estilo
+ num_words: int
+
+ if style_words.nombre_estilo == "atributos":
+ num_words = EsAtributo.objects.get(
+ id_tecnica=self.sensorial_session.tecnica).palabras.count()
+ elif style_words.nombre_estilo == "vocabulario":
+ num_words = EsVocabulario.objects.get(
+ id_tecnica=self.sensorial_session.tecnica).id_vocabulario.palabras.count()
+
+ return num_products * num_words
+
+ def checkAllParticipantsEnded(self):
+ technique = self.sensorial_session.tecnica
+
+ expected_ratings_repetition = self.getExpectedRatings()
+
+ all_participations = ParticipacionController.getParticipationsInTechinique(
+ technique=technique)
+
+ if len(all_participations) < technique.limite_catadores:
+ return (False, "No se ha alcanzado el número máximo de Catadores")
+
+ for particiapation in all_participations:
+ num_ratings_now = Calificacion.objects.filter(
+ id_tecnica=technique, id_catador=particiapation.catador, num_repeticion=technique.repeticion).count()
+
+ if num_ratings_now < expected_ratings_repetition:
+ return (False, "No todos los catadores han finalizado su evaluación")
+
+ return (True, "Puedes finalizar la sesión")
+
+ def finishSession(self):
+ response = SesionController.finishRepetion(self.sensorial_session)
+ if isinstance(response, dict):
+ return controller_error(response["error"])
+ self.updataSession()
+ return self.sensorial_session
diff --git a/tecnicas/controllers/views_controller/tester_list_controller.py b/tecnicas/controllers/views_controller/tester_list_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..85a10afb61e12375a384e7db538aacd5d6b54734
--- /dev/null
+++ b/tecnicas/controllers/views_controller/tester_list_controller.py
@@ -0,0 +1,58 @@
+from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
+from tecnicas.models import Catador
+from tecnicas.utils import controller_error
+
+
+class TesterListController():
+ def __init__(self, page: int):
+ self.num_page = page
+
+ def getContext(self):
+ view_context = {"num_page": self.num_page}
+
+ res_tuple = TesterListController.getTestersByPage(page=self.num_page)
+
+ if isinstance(res_tuple, dict):
+ view_context["error"] = res_tuple["error"]
+ return view_context
+
+ (testers, last_page) = res_tuple
+
+ view_context["testers"] = testers
+ view_context["last_page"] = last_page
+
+ return view_context
+
+ @staticmethod
+ def getTestersByPage(page: int):
+ elements_by_page = 6
+
+ queryset = (
+ Catador.objects
+ .select_related(
+ "user"
+ )
+ .only(
+ "user__first_name",
+ "user__last_name",
+ "user__username",
+ "user__email",
+ "genero",
+ "telefono",
+ "nacimiento"
+ )
+ .order_by("-user__date_joined")
+ )
+
+ paginator = Paginator(queryset, elements_by_page)
+ try:
+ testers_in_page = paginator.page(page)
+ except PageNotAnInteger:
+ return controller_error("índice inválido")
+
+ if not testers_in_page.object_list:
+ return controller_error("Sin registros de Catadores")
+
+ is_last_page = not testers_in_page.number < paginator.num_pages
+
+ return (testers_in_page, is_last_page)
\ No newline at end of file
diff --git a/tecnicas/decorators/required_presenter.py b/tecnicas/decorators/required_presenter.py
index c7c02f16a5df7b4c1a1fd083c6faa7cce7dc8267..b86849ad4cf9c8b02d2928fe0be95aa8adbcd031 100644
--- a/tecnicas/decorators/required_presenter.py
+++ b/tecnicas/decorators/required_presenter.py
@@ -8,7 +8,7 @@ def required_presenter(view_func):
if not request.user.is_authenticated:
return redirect("cata_system:autenticacion")
- if not hasattr(request.user, "presentador"):
+ if not hasattr(request.user, "user_presentador"):
raise PermissionDenied(
"Solo los presentadores pueden acceder a esta vista")
return view_func(request, *args, **kwargs)
diff --git a/tecnicas/forms/catador_form.py b/tecnicas/forms/catador_form.py
index 98e87e370f290ec28232622d8bc3f93bdc4475c6..465abd0de2e2ee7c2a3d976a8cff028dfde3340f 100644
--- a/tecnicas/forms/catador_form.py
+++ b/tecnicas/forms/catador_form.py
@@ -1,25 +1,148 @@
from django import forms
-from ..models import Catador
-
-class CatadorForm(forms.ModelForm):
- class Meta:
- model = Catador
- fields = "__all__"
- widgets = {
- "usuarioCatador": forms.TextInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
- "nombre": forms.TextInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
- "apellido": forms.TextInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
- "telefono": forms.NumberInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general", "max": "10", "min": "10" } ),
- "correo": forms.EmailInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general" } ),
- "fechaNacimiento": forms.DateInput( attrs={ "class": "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-general", "type": "date", } ),
+from django.contrib.auth.models import User
+from django.core.validators import RegexValidator, EmailValidator
+
+
+class CatadorForm(forms.Form):
+ styles_input = "ct-inputs-pos-cata bg-surface-ligt text-center w-full p-1 rounded-lg text-black disabled:bg-surface-alter-card"
+
+ nombre_usuario = forms.CharField(
+ label="Nombre de usuario",
+ max_length=30,
+ min_length=5,
+ required=True,
+ validators=[
+ RegexValidator(
+ regex=r'^(?![0-9._])[A-Za-z0-9._]+$',
+ message=(
+ "El nombre de usuario debe iniciar con una letra y solo puede "
+ "contener letras, números, puntos y guiones bajos."
+ ),
+ code='invalid_username'
+ )
+ ],
+ widget=forms.TextInput(
+ attrs={
+ "placeholder": "Ej. mario_hugo23",
+ "class": styles_input,
+ }
+ )
+ )
+
+ nombre = forms.CharField(
+ label="Nombre del catador",
+ max_length=50,
+ required=True,
+ validators=[
+ RegexValidator(
+ regex=r'^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$',
+ message="El nombre solo puede contener letras y espacios."
+ )
+ ],
+ widget=forms.TextInput(
+ attrs={
+ "placeholder": "Ej. Mario",
+ "class": styles_input,
+ }
+ )
+ )
+
+ apellido = forms.CharField(
+ label="Apellido del catador",
+ max_length=50,
+ required=True,
+ validators=[
+ RegexValidator(
+ regex=r'^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$',
+ message="El apellido solo puede contener letras y espacios."
+ )
+ ],
+ widget=forms.TextInput(
+ attrs={
+ "placeholder": "Ej. Sánchez",
+ "class": styles_input,
+ }
+ )
+ )
+
+ telefono = forms.CharField(
+ label="Teléfono",
+ max_length=10,
+ required=True,
+ validators=[
+ RegexValidator(
+ regex=r'^\d{10}$',
+ message="El teléfono debe contener solo números (puede incluir + al inicio)."
+ )
+ ],
+ widget=forms.TextInput(
+ attrs={
+ "placeholder": "Ej. 5512345678",
+ "class": styles_input,
+ "type": "tel",
+ }
+ )
+ )
+
+ correo = forms.EmailField(
+ label="Correo electrónico",
+ required=True,
+ validators=[EmailValidator(
+ message="Introduce un correo electrónico válido.")],
+ widget=forms.EmailInput(
+ attrs={
+ "placeholder": "Ej. mario@example.com",
+ "class": styles_input,
+ }
+ )
+ )
+
+ fecha_nacimiento = forms.DateField(
+ label="Fecha de nacimiento",
+ required=True,
+ widget=forms.DateInput(
+ attrs={
+ "type": "date",
+ "class": styles_input,
+ }
+ ),
+ error_messages={
+ "invalid": "Introduce una fecha válida (DD-MM-YYYY)."
}
+ )
+
+ GENERO_OPCIONES = [
+ ('Hombre', 'Hombre'),
+ ('Mujer', 'Mujer')
+ ]
+
+ genero = forms.ChoiceField(
+ label="Género",
+ required=True,
+ choices=GENERO_OPCIONES,
+ widget=forms.RadioSelect(
+ attrs={
+ "class": "ct-inputs-pos-cata radio radio-warning checked:bg-btn-secondary mx-2 bg-surface-ligt disabled:bg-ct-error"
+ }
+ )
+ )
+
+ is_update = forms.BooleanField(
+ initial=False,
+ required=False,
+ widget=forms.HiddenInput()
+ )
def clean(self):
- data_cleand = super().clean()
-
- phone = data_cleand["telefono"]
- size_phone = len(str(abs(phone)))
- print("tamano de telefono", size_phone)
- if size_phone != 10:
- self.add_error("telefono", "telefono debe tener 10 digitos")
- return data_cleand
\ No newline at end of file
+ cleaned_data = super().clean()
+ username = cleaned_data.get("nombre_usuario")
+ is_update = cleaned_data.get("is_update")
+
+ print(username, is_update)
+
+ if not is_update:
+ if User.objects.filter(username__iexact=username).exists():
+ raise forms.ValidationError(
+ "Este nombre de usuario ya está registrado.")
+
+ return cleaned_data
diff --git a/tecnicas/forms/codes_form.py b/tecnicas/forms/codes_form.py
index 575c46221fd2f63420973438df247c5f1328bb2b..5d3a1c0998a7d4c13a856fb2770945f8be2067c5 100644
--- a/tecnicas/forms/codes_form.py
+++ b/tecnicas/forms/codes_form.py
@@ -1,10 +1,11 @@
from django import forms
+
class CodesForm(forms.Form):
- def __init__(self, *args, codes = [], **kwargs):
+ def __init__(self, *args, codes=[], **kwargs):
super().__init__(*args, **kwargs)
for index, code in enumerate(codes):
self.fields[f'producto_{index+1}'] = forms.CharField(max_length=3, required=True, min_length=3, initial=code, label=f"codigo {index+1}", widget=forms.TextInput(attrs={
"class": "ct-code bg-surface-ligt p-1 border-b-1 text-center w-full disabled:bg-surface-general uppercase"
- }))
\ No newline at end of file
+ }))
diff --git a/tecnicas/forms/sesion_basic_form.py b/tecnicas/forms/sesion_basic_form.py
index b724c261ae20f01e8f8d869611a22a9dae39e6df..87529c24fbe3cdcfbdcc4f4353d631d81ce9a189 100644
--- a/tecnicas/forms/sesion_basic_form.py
+++ b/tecnicas/forms/sesion_basic_form.py
@@ -5,7 +5,8 @@ from ..models import EstiloPalabra
class SesionBasicForm(forms.Form):
- id_tecnica = forms.IntegerField(widget=forms.HiddenInput())
+ sizes_structure = [5, 7, 9]
+ sizes_continue = [9, 13, 15]
nombre_sesion = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
"class": "bg-surface-ligt border-b-1 text-center w-full p-1",
@@ -28,36 +29,37 @@ class SesionBasicForm(forms.Form):
"placeholder": "Solo números"
}), required=True)
- tamano_escala = forms.IntegerField(widget=forms.NumberInput(attrs={
- "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
- }), required=True, min_value=5)
-
instrucciones = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
"class": "bg-surface-ligt border-b-1 text-center w-full p-1",
"placeholder": "Este campo es opcional"
}), required=False)
- def __init__(self, *args, id_tecnica_new=0, **kwargs):
+ def __init__(self, *args, initial_conf: dict = None, **kwargs):
super().__init__(*args, **kwargs)
+ if initial_conf is None:
+ initial_conf = {}
+
self.fields['estilo_palabras'] = forms.ModelChoiceField(queryset=EstiloPalabra.objects.all(), widget=forms.RadioSelect(attrs={
- "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
+ "class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
}), required=True, initial=EstiloPalabra.objects.first())
self.fields['tipo_escala'] = forms.ModelChoiceField(queryset=TipoEscala.objects.all(), widget=forms.RadioSelect(attrs={
"class": "uppercase text-lg tracking-wider font-medium p-2 px-4 active:px-5 transition-all rounded-xl bg-blue-500 text-white",
}), required=True, initial=TipoEscala.objects.first())
- if id_tecnica_new != 0:
- self.fields['id_tecnica'] = forms.IntegerField(
- initial=id_tecnica_new, widget=forms.HiddenInput())
+ self.fields['tamano_escala'] = forms.IntegerField(widget=forms.HiddenInput(attrs={
+ "class": "cts-size-input",
+ }), required=True)
- def clean(self):
- data_clean = super().clean()
+ if "numero_catadores" in initial_conf:
+ self.fields["numero_catadores"].initial = initial_conf["numero_catadores"]
- sizes_estruturada = [5, 7, 9]
- sizes_continua = [9, 12, 15]
+ if "numero_repeticiones" in initial_conf:
+ self.fields["numero_repeticiones"].initial = initial_conf["numero_repeticiones"]
+ def clean(self):
+ data_clean = super().clean()
escala = data_clean.get("tipo_escala")
if escala and not isinstance(escala, TipoEscala):
@@ -70,14 +72,7 @@ class SesionBasicForm(forms.Form):
tamano_escala = data_clean.get("tamano_escala")
- if escala.nombre_escala == "estructurada" and not sizes_estruturada.__contains__(tamano_escala):
+ if escala.nombre_escala == "estructurada" and not self.sizes_structure.__contains__(tamano_escala):
self.add_error("tamano_escala", "El tamaño de la escala no aplica")
- elif escala.nombre_escala == "continua" and not sizes_continua.__contains__(tamano_escala):
+ elif escala.nombre_escala == "continua" and not self.sizes_continue.__contains__(tamano_escala):
self.add_error("tamano_escala", "El tamaño de la escala no aplica")
-
- id_tecnica = data_clean.get("id_tecnica")
-
- try:
- tecnica = TipoTecnica.objects.get(pk=id_tecnica)
- except (ValueError, TipoTecnica.DoesNotExist):
- return data_clean
diff --git a/tecnicas/forms/sesion_tags_form.py b/tecnicas/forms/sesion_tags_form.py
index 7f55b05513a97ba9037767f5e3dee5ffccf724e8..8b2c85603f4bed97438a7ff16a57132cfdfddbda 100644
--- a/tecnicas/forms/sesion_tags_form.py
+++ b/tecnicas/forms/sesion_tags_form.py
@@ -1,25 +1,35 @@
from django import forms
-
from ..models import Etiqueta
+
class SesionTagsForm(forms.Form):
- def __init__(self, *args, longitud=None, tipo_escala:str=None, **kwargs):
+ available_struture_tags = {
+ "5": ["percepcion nula", "percepcion moderada", "se puede percibir",
+ "percepcion intensa", "percepcion total"],
+ "7": ["percepcion nula", "ligera percepcion", "percepcion moderada", "se puede percibir", "buena percepcion",
+ "percepcion intensa", "percepcion total"],
+ "9": ["percepcion nula", "ligera percepcion", "poca percepcion", "percepcion moderada", "se puede percibir", "buena percepcion",
+ "percepcion intensa", "percepcion muy intensa", "percepcion total"]
+ }
+
+ def __init__(self, *args, longitud=None, tipo_escala: str = None, **kwargs):
super().__init__(*args, **kwargs)
if tipo_escala == "estructurada":
+ use_tags = self.available_struture_tags[f"{longitud}"]
for i in range(longitud):
- self.fields[f'segmento_{i+1}'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label=f"segmento {i+1}", empty_label="Selecione opcion", widget=forms.Select(attrs={
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
+ self.fields[f'segmento_{i+1}'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta=use_tags[i]), required=True, label=f"segmento {i+1}", empty_label="Selecione opcion", widget=forms.Select(attrs={
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
}))
else:
- self.fields['punto_inicial'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label="Punto inicial", empty_label="Selecione opcion", widget=forms.Select(attrs={
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
- }))
-
- self.fields['punto_medio'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label="Punto medio", empty_label="Selecione opcion", widget=forms.Select(attrs={
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
- }))
-
- self.fields['punto_final'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), required=True, label="Punto final", empty_label="Selecione opcion", widget=forms.Select(attrs={
- "class":"ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
- }))
\ No newline at end of file
+ self.fields['punto_inicial'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta="percepcion nula"), required=True, label="Punto inicial", empty_label="Selecione opcion", widget=forms.Select(attrs={
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
+ }))
+
+ self.fields['punto_medio'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta="se puede percibir"), required=True, label="Punto medio", empty_label="Selecione opcion", widget=forms.Select(attrs={
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
+ }))
+
+ self.fields['punto_final'] = forms.ModelChoiceField(queryset=Etiqueta.objects.all(), initial=Etiqueta.objects.get(valor_etiqueta="percepcion total"), required=True, label="Punto final", empty_label="Selecione opcion", widget=forms.Select(attrs={
+ "class": "ct-select-op p-1 max-sm:w-full bg-surface-ligt [*]:capitalize"
+ }))
diff --git a/tecnicas/middlewares/presenter_middleware.py b/tecnicas/middlewares/presenter_middleware.py
index 83d71b9ef1062489c1c21400efe92c830c4804a5..4180c589ae4ca5134625de891b76167bb52d9c45 100644
--- a/tecnicas/middlewares/presenter_middleware.py
+++ b/tecnicas/middlewares/presenter_middleware.py
@@ -1,18 +1,17 @@
-from django.core.exceptions import PermissionDenied
-
class PresenterAccessMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
path = request.path_info
-
+
if path.startswith('/cata/presenter/'):
if not request.user.is_authenticated:
from django.shortcuts import redirect
return redirect("cata_system:autenticacion")
if not hasattr(request.user, 'user_presentador'):
+ from django.shortcuts import redirect
return redirect("cata_system:autenticacion")
return self.get_response(request)
diff --git a/tecnicas/middlewares/tester_middleware.py b/tecnicas/middlewares/tester_middleware.py
index 21266100e0761e55dc2f6f7e4d5bb23bb29c65fa..b76339208dec73ad613505c2918e5e2726a88e4a 100644
--- a/tecnicas/middlewares/tester_middleware.py
+++ b/tecnicas/middlewares/tester_middleware.py
@@ -9,38 +9,13 @@ class TesterAccessMiddleware:
def __call__(self, request):
path = request.path_info
- if path.startswith('/cata/tester/'):
+ if path.startswith('/cata/testers/'):
if not request.user.is_authenticated:
from django.shortcuts import redirect
return redirect("cata_system:catador_login")
- if not hasattr(request.user, 'catador'):
+ if not hasattr(request.user, 'user_catador'):
+ from django.shortcuts import redirect
return redirect("cata_system:catador_login")
return self.get_response(request)
-
-# from django.http import HttpRequest
-
-
-# class LoginTesterMiddleware():
-# def __init__(self, get_response):
-# self.get_response = get_response
-
-# def __call__(self, req: HttpRequest):
-# base_url_protected = "/cata/testers/"
-
-# if req.path.startswith(base_url_protected):
-# if not "cata_username" in req.session:
-# id_participacion = req.COOKIES.get("id_participacion")
-# if id_participacion:
-# from tecnicas.controllers import ParticipacionController
-# ParticipacionController.outSession(id_participacion)
-# from django.shortcuts import redirect
-# from django.urls import reverse
-# response = redirect(reverse("cata_system:catador_login"))
-# response.delete_cookie("id_participacion")
-# return response
-
-# response = self.get_response(req)
-
-# return response
diff --git a/tecnicas/models/participacion.py b/tecnicas/models/participacion.py
index f6ff79e8fa847a93ce60b5fc5a3c678659d25570..8086129c7a6395539b5fbdc0d37f1a5f591218f9 100644
--- a/tecnicas/models/participacion.py
+++ b/tecnicas/models/participacion.py
@@ -12,4 +12,4 @@ class Participacion(models.Model):
finalizado = models.BooleanField(default=True)
def __str__(self):
- return f"{self.catador.usuarioCatador} {'activo' if self.activo else 'no activo'}"
+ return f"{self.catador.user.username} {'activo' if self.activo else 'no activo'}"
diff --git a/tecnicas/models/sesion_sensorial.py b/tecnicas/models/sesion_sensorial.py
index ee9d0d9d291164b7e3af4366380ca92bddd94c58..466b9786687ebf725f1a505f2667fae19013de5b 100644
--- a/tecnicas/models/sesion_sensorial.py
+++ b/tecnicas/models/sesion_sensorial.py
@@ -17,4 +17,9 @@ class SesionSensorial(models.Model):
Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
def __str__(self):
- return self.nombre_sesion if self.nombre_sesion else self.codigo_sesion
+ name_str = f"{self.codigo_sesion}"
+
+ if self.nombre_sesion:
+ name_str += f" : {self.nombre_sesion}"
+
+ return name_str
diff --git a/tecnicas/static/js/catador-craete.js b/tecnicas/static/js/catador-craete.js
index 40d30dd830db370d8b4ee98162a0ad0293876175..4747e8d2270d4d3e59b3b56731375795125dc193 100644
--- a/tecnicas/static/js/catador-craete.js
+++ b/tecnicas/static/js/catador-craete.js
@@ -7,7 +7,6 @@ for (let index = 0; index < inputs.length; index++) {
function hiddenErrorAndMessages(e) {
for (let index = 0; index < notifiactions.length; index++) {
- console.log("Oculto");
notifiactions.item(index).classList.add("hidden");
}
}
diff --git a/tecnicas/static/js/create-session.js b/tecnicas/static/js/create-session.js
index 8867ca652be94fdf45efd05a3ea5f2492fb9323f..4e1ea119bb960aae102f37c55ac53f7b81e777f0 100644
--- a/tecnicas/static/js/create-session.js
+++ b/tecnicas/static/js/create-session.js
@@ -90,7 +90,7 @@ function renderElementsResponse({
];
const aIndex = document.createElement("a");
- aIndex.href = "/cata";
+ aIndex.href = "/cata/presenter/";
aIndex.textContent = "Volver al inicio";
aIndex.classList.add(
@@ -99,7 +99,7 @@ function renderElementsResponse({
);
const aDetails = document.createElement("a");
- aDetails.href = `/cata/detalles-sesion/${sessionId}`;
+ aDetails.href = `/cata/presenter/detalles-sesion/${sessionId}`;
aDetails.textContent = "Ver detalles la sesion";
aDetails.classList.add(
diff --git a/tecnicas/static/js/details-session.js b/tecnicas/static/js/details-session.js
index 512af78cb783cb635cf227e2ac5519e6151686ac..fe403c00daa302d9ab518c778aa56d03d87d98bb 100644
--- a/tecnicas/static/js/details-session.js
+++ b/tecnicas/static/js/details-session.js
@@ -4,23 +4,17 @@ const notificationError = document.querySelector(".ct-notification-error");
if (notificationError) {
setTimeout(function () {
notificationError.classList.add("hidden");
- }, 2000);
+ }, 3000);
}
function startRepetition() {
- const inputAction = document.createElement("input");
- inputAction.type = "hidden";
- inputAction.name = "action";
- inputAction.value = "start_session";
-
- const inputUser = document.createElement("input");
- inputUser.type = "hidden";
- inputUser.name = "username";
- inputUser.value = "aguBido";
-
- actionForm.appendChild(inputAction);
- actionForm.appendChild(inputUser);
-
- actionForm.classList.remove("hidden");
+ const input = actionForm.querySelector(".action-option")
+ input.value = "start_session";
actionForm.submit();
}
+
+function deleteSession() {
+ const input = actionForm.querySelector(".action-option")
+ input.value = "delete_session";
+ actionForm.submit();
+}
\ No newline at end of file
diff --git a/tecnicas/static/js/panel-basic.js b/tecnicas/static/js/panel-basic.js
index be21f50255ca95689f760d4858b43dec4e954e47..7520d294328b676bc274ccb3a65d30edadb8744b 100644
--- a/tecnicas/static/js/panel-basic.js
+++ b/tecnicas/static/js/panel-basic.js
@@ -1,6 +1,6 @@
const descriptons = {
- estructurada: ["Establece número de segmentos:", "Puede ser 5, 7 o 9"],
- continua: ["Establece la longitud de la escala:", "Puede ser 9, 12 o 15"],
+ estructurada: "Establece número de segmentos:",
+ continua: "Establece la longitud de la escala:",
atributos:
"Con el estilo atributos elijes las palabras para evaluar los productos",
vocabulario:
@@ -12,6 +12,11 @@ let inputTamano;
let inputsStyle;
let helpStyle;
+let sizeOptionsContainer;
+const SIZE_OPTIONS = {
+ estructurada: [5, 7, 9],
+ continua: [9, 13, 15],
+};
initPanel();
@@ -22,6 +27,7 @@ function initPanel() {
function initRadios() {
inputsScale = document.getElementsByName("tipo_escala");
inputTamano = document.getElementsByName("tamano_escala").item(0);
+ sizeOptionsContainer = document.getElementsByClassName("cts-options-size-scale")[0];
for (let index = 0; index < inputsScale.length; index++) {
let parent = inputsScale.item(index).parentElement;
@@ -46,6 +52,73 @@ function initRadios() {
showDescriptionStyle(parent);
}
}
+
+ initSizeOptions();
+}
+
+function initSizeOptions() {
+ for (let i = 0; i < inputsScale.length; i++) {
+ const radio = inputsScale.item(i);
+ radio.addEventListener('change', () => {
+ const tag = getTagFromLabel(radio);
+ populateSizeOptions(tag);
+ });
+ if (radio.checked) {
+ const tag = getTagFromLabel(radio);
+ populateSizeOptions(tag);
+ }
+ }
+
+ const form = document.querySelector('form');
+ if (!form) return;
+
+ form.addEventListener('submit', (e) => {
+ const chosen = document.querySelector('input[name="option_size_scale"]:checked');
+ if (chosen && inputTamano) {
+ inputTamano.value = chosen.value;
+ }
+
+ if (sizeOptionsContainer) {
+ const toRemove = sizeOptionsContainer.querySelectorAll('input[name="option_size_scale"]');
+ toRemove.forEach((el) => el.remove());
+ }
+ });
+}
+
+function getTagFromLabel(radio) {
+ try {
+ const parent = radio.parentElement;
+ if (!parent) return '';
+ const text = parent.textContent || '';
+ return text.trim().split(/\s+/)[0].toLowerCase();
+ } catch (err) {
+ return '';
+ }
+}
+
+function populateSizeOptions(tag) {
+ const options = SIZE_OPTIONS[tag] || SIZE_OPTIONS['estructurada'];
+ if (!sizeOptionsContainer) return;
+ sizeOptionsContainer.innerHTML = '';
+
+ options.forEach((val) => {
+ const label = document.createElement('label');
+ label.className = 'flex flex-col items-center cursor-pointer';
+
+ const input = document.createElement('input');
+ input.type = 'radio';
+ input.name = 'option_size_scale';
+ input.value = String(val);
+ input.className = 'radio radio-lg checked:bg-pink-500';
+
+ const span = document.createElement('span');
+ span.className = 'mt-2 text-xl text-gray-700 font-medium';
+ span.textContent = String(val);
+
+ label.appendChild(input);
+ label.appendChild(span);
+ sizeOptionsContainer.appendChild(label);
+ });
}
function showDescriptionStyle(label) {
@@ -56,6 +129,5 @@ function showDescriptionStyle(label) {
function showDescriptionTamanoScale(label) {
const text = label.textContent.trim();
let parent = inputTamano.parentElement;
- parent.getElementsByTagName("p")[0].textContent = descriptons[text][0];
- inputTamano.placeholder = descriptons[text][1];
+ parent.getElementsByTagName("p")[0].textContent = descriptons[text];
}
diff --git a/tecnicas/static/js/panel-words.js b/tecnicas/static/js/panel-words.js
index d86d9aa0c15dd1df49e6c0a66ebbf9d2f70eac6b..2a68fefff6f0e6076562a5282eff694ab4810239 100644
--- a/tecnicas/static/js/panel-words.js
+++ b/tecnicas/static/js/panel-words.js
@@ -28,7 +28,7 @@ async function getWordsByName(e) {
palabra: dataForm.get("search").trim(),
});
- const url = `api/palabras?${params}`;
+ const url = `/cata/api/palabras?${params}`;
try {
const respone = await fetch(url, {
diff --git a/tecnicas/static/js/showHiddenElement.js b/tecnicas/static/js/showHiddenElement.js
new file mode 100644
index 0000000000000000000000000000000000000000..8c01000129d3408ed29b60b0f1b75b912baefdf0
--- /dev/null
+++ b/tecnicas/static/js/showHiddenElement.js
@@ -0,0 +1,9 @@
+function hiddenWarningDialog(styleClass) {
+ const element = document.querySelector(`.${styleClass}`)
+ element.classList.add("hidden")
+}
+
+function showWarningDialog(styleClass) {
+ const element = document.querySelector(`.${styleClass}`)
+ element.classList.remove("hidden")
+}
\ No newline at end of file
diff --git a/tecnicas/templates/tecnicas/auth.html b/tecnicas/templates/tecnicas/auth.html
index 093858a788307c7fb462b05d8b49938a4064c775..9e32d69bdac1fd8987a9a4b18efab0778743e3f2 100644
--- a/tecnicas/templates/tecnicas/auth.html
+++ b/tecnicas/templates/tecnicas/auth.html
@@ -26,7 +26,7 @@
diff --git a/tecnicas/templates/tecnicas/cata-login.html b/tecnicas/templates/tecnicas/cata-login.html
index c128c8f176795fbf32791d0f810d2e3e9f0256dc..890571ac642ddaa89cb360d03c93209a52f89801 100644
--- a/tecnicas/templates/tecnicas/cata-login.html
+++ b/tecnicas/templates/tecnicas/cata-login.html
@@ -13,23 +13,15 @@
{% if error %}
-
- {{ error }}
-
+ Usuario: + {{ tester.user.username }} +
+ ++ Genero: + {{ tester.genero }} +
++ Teléfono: + {{ tester.telefono }} +
++ Email: + {{ tester.user.email }} +
++ Nacimiento: + {{ tester.nacimiento|date:"d/m/Y" }} +
+Código: {{ session.codigo_sesion }}
+Técnica: {{ session.tecnica.tipo_tecnica }}
+Fecha: {{ session.fechaCreacion }}
++ Finazliado: + {% if session.participacion_finalizado %} + Se ha concluido + {% else %} + Sin concluir + {% endif %} +
++ Estatus: + {% if session.activo %} + En proceso + {% else %} + No activo + {% endif %} +
+Género:
+- {{ error }} -
+ {% include "../components/error-message.html" with message=error %} {% endif %} {% if message %} -- {{ message }} -
+ {% include "../components/error-message.html" with message=message %} {% endif %} +nombre del catador:
{{ form_cata.nombre }} + {% if form_cata.nombre.errors %} + + {{ form_cata.nombre.errors}} + + {% endif %} - +Género:
+