diff --git a/cata_system/settings.py b/cata_system/settings.py
index 56e4dbaa1602b8c35951ae70fb14eacc2681f61f..12854f8ab3c031d63757349411b19eb48cfc2981 100644
--- a/cata_system/settings.py
+++ b/cata_system/settings.py
@@ -25,7 +25,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = 'django-insecure-u)t2290d+c@$$7@!%@m&=w44$a@haxew8$!!tggh!up-+0-ll('
+SECRET_KEY = os.getenv("DJANGO_SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..399835bc716702fb61d3576d01b6b3bd132a895c
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,7 @@
+services: # Definicion de servicios. Se convertiran en contendores [Requerido]
+ app-django-cata-system: # Nombre del servicio para control de Docker Compose [Requerido]
+ hostname: django-cata-system # Nombre del host, [No requerido]
+ container_name: django-cata-system # Nombre del contenedor para Docker [No requerido]
+ build: .
+ ports: # Mapeo de puertos -> sintaxis= puerto del host físico (no debe estar ocupado al momento de crear el contenedor):puerto del contendor [No requerido, depende de si necesita conectividad al exterior]
+ - "9495:7860" # -"[host:]container[/protocol]"
\ No newline at end of file
diff --git a/tecnicas/admin.py b/tecnicas/admin.py
index f998641a9ac4a47aedc21313103f762cdcb01c42..44cd4bff89696421eb2f4bcfc134e5b20e34e65c 100644
--- a/tecnicas/admin.py
+++ b/tecnicas/admin.py
@@ -6,7 +6,7 @@ from .models import Catador, Presentador
from .models import Tecnica, SesionSensorial
-from .models import EsAtributo, Palabra
+from .models import EsAtributo, Palabra, Vocabulario
from .models import Etiqueta, Escala, EtiquetasEscala
@@ -31,6 +31,7 @@ admin.site.register(SesionSensorial)
admin.site.register(EsAtributo)
admin.site.register(Palabra)
+admin.site.register(Vocabulario)
admin.site.register(Escala)
admin.site.register(EtiquetasEscala)
diff --git a/tecnicas/controllers/__init__.py b/tecnicas/controllers/__init__.py
index 41d8a06586110a47b5df41ef29756f8fa418421c..8e61f1aca6b63b7d8b535b90382d9bbd48311d34 100644
--- a/tecnicas/controllers/__init__.py
+++ b/tecnicas/controllers/__init__.py
@@ -12,12 +12,6 @@ from .models_controller.posicion_controller import PosicionController
from .models_controller.particiapacion_controller import ParticipacionController
from .models_controller.dato_controller import DatoController
-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.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
@@ -28,4 +22,16 @@ from .views_controller.session_management.details_controller import DetallesCont
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
+from .views_controller.session_management.monitor_escalas_controller import MonitorEscalasController
+from .views_controller.session_management.monitor_rata_controller import MonitorRATAController
+
+from .views_controller.sessions_tester.login_session_tester_controller import LoginSessionTesterController
+from .views_controller.sessions_tester.init_session_tester_controller import InitSessionTesterController
+from .views_controller.sessions_tester.convencional_scales_controller import ConvencionalScalesController
+from .views_controller.sessions_tester.list_sessions_tester_controller import ListSessionsTesterController
+
+from .views_controller.vocabulary_manage.create_vocabulary_controller import CreateVocabularyController
+from .views_controller.vocabulary_manage.list_vocabulary_controller import ListVocabularyController
+
+from .views_controller.api_rating_controller import ApiRatingController
+from .views_controller.tester_list_controller import TesterListController
diff --git a/tecnicas/controllers/models_controller/calificacion_controller.py b/tecnicas/controllers/models_controller/calificacion_controller.py
index a99f63d3ddc9ff1d3326a59e82656648a8dee480..5e21f3ccc03122a99ebbe41c42a375a2343246e0 100644
--- a/tecnicas/controllers/models_controller/calificacion_controller.py
+++ b/tecnicas/controllers/models_controller/calificacion_controller.py
@@ -6,49 +6,22 @@ from tecnicas.utils import controller_error, getId
class CalificacionController():
def __init__(self, product: Producto | int, technique: Tecnica | int, tester: Catador | int):
+ technique = Tecnica.objects.only(
+ "repeticion").get(id=getId(technique))
+
atributes = {
- "num_repeticion": 0,
- "id_tecnica_id": getId(technique),
+ "num_repeticion": technique.repeticion,
+ "id_tecnica": technique,
"id_producto_id": getId(product),
"id_catador_id": getId(tester),
}
- self.rating = Calificacion(**atributes)
-
- def validateRating(self):
- try:
- self.rating.clean()
- return self.rating
- except ValidationError as e:
- return controller_error("No es posible validar la calificación")
-
- def setRepetition(self, repetition: int = None) -> int | dict:
- try:
- if repetition is not None:
- self.rating.num_repeticion = repetition
- else:
- self.rating.num_repeticion = self.rating.id_tecnica.repeticion
-
- return self.rating.num_repeticion
- except ValidationError as e:
- return controller_error(e)
-
- def saveRating(self):
- try:
- self.rating.save()
- return self.rating
- except ValidationError as e:
- return controller_error(e)
+ (self.rating, created) = Calificacion.objects.get_or_create(**atributes)
@staticmethod
def getRatingsByTechnique(technique: Tecnica):
repetition = technique.repeticion
-
- if not repetition:
- return controller_error("Sin datos calificados aún")
-
ratings = list(Calificacion.objects.filter(id_tecnica=technique))
-
return ratings
@staticmethod
@@ -94,39 +67,53 @@ class CalificacionController():
return ratings
@staticmethod
- def checkProducsWithoutRating(
+ def checkPositionWithoutRating(
positions: list[Posicion] = None,
- user_cata: str = None,
+ user_cata: Catador | str = None,
repetition: int = None,
- technique: Tecnica = None,
- id_technique: int = None,
+ technique: Tecnica | int = None,
num_words: int = None):
- check_products = [position.id_producto for position in positions]
+ end_rating = False
+
+ # Obtener lista con codigos de productos
+ check_products = [
+ position.id_producto.codigoProducto for position in positions]
filters = {
- "user_tester": user_cata,
- "repetition": repetition
+ "num_repeticion": repetition,
+ "id_tecnica": getId(technique)
}
- if technique is not None:
- filters["technique"] = technique
- elif id_technique is not None:
- filters["id_technique"] = id_technique
+ if isinstance(user_cata, Catador):
+ filters["id_catador"] = user_cata
+ else:
+ filters["id_catador"] = Catador.objects.get(
+ user__username=user_cata)
- ratings = CalificacionController.getRatings(**filters)
+ ratings = list(Calificacion.objects.filter(**filters).select_related(
+ "id_producto",
+ "id_tecnica",
+ "id_catador",
+ ))
+ # Si no hay calificaciones regresar las posiciones
if len(ratings) == 0:
- return positions
+ return (positions, end_rating)
- ratings_dict = defaultdict(list)
+ rating_products = [
+ rating.id_producto.codigoProducto for rating in ratings]
- for rat in ratings:
- ratings_dict[rat.id_producto.id].append(rat)
+ for index, rating in enumerate(ratings):
+ data_rating = rating.dato_calificacion.all()
- for index, product in enumerate(check_products):
- ratings_of_product = ratings_dict.get(product.id, [])
+ if len(data_rating) < num_words or len(data_rating) == 0:
+ return (positions[index], end_rating)
- if len(ratings_of_product) < num_words or len(ratings_of_product) == 0:
- return positions[index]
-
- return controller_error("Sin productos por calificar")
+ next_product = list(set(check_products) - set(rating_products))
+ if len(next_product) != 0:
+ next_position = [
+ position for position in positions if position.id_producto.codigoProducto == next_product[0]][0]
+ return (next_position, end_rating)
+ else:
+ end_rating = True
+ return (positions[-1], end_rating)
diff --git a/tecnicas/controllers/models_controller/dato_controller.py b/tecnicas/controllers/models_controller/dato_controller.py
index 019cd4d731ff2a5e5f9efec921475995276fc89e..9afcc498d6458644344f187b4bb68c9d6c74a608 100644
--- a/tecnicas/controllers/models_controller/dato_controller.py
+++ b/tecnicas/controllers/models_controller/dato_controller.py
@@ -12,7 +12,11 @@ class DatoController():
}
self.data = Dato(**atributes)
- self.value_data = ValorDecimal(valor=value_rating)
+
+ if isinstance(value_rating, bool):
+ self.value_data = ValorBooleano(valor=value_rating)
+ else:
+ self.value_data = ValorDecimal(valor=value_rating)
def setRating(self, new_rating: Calificacion):
try:
@@ -35,19 +39,14 @@ class DatoController():
except ValidationError as e:
return controller_error(e.message)
- def setInstanceValue(self):
- technique = self.data.id_calificacion.id_tecnica
-
- if technique.tipo_tecnica == "cata":
- self.value_data = ValorBooleano(
- id_dato=self.data,
- valor=self.value_data.valor
- )
+ def setValue(self, new_value=None):
+ if new_value:
+ if isinstance(new_value, bool):
+ self.value_data = ValorBooleano(valor=new_value)
+ else:
+ self.value_data = ValorDecimal(valor=new_value)
else:
- self.value_data = ValorDecimal(
- id_dato=self.data,
- valor=self.value_data.valor
- )
+ self.value_data.id_dato = self.data
return self.value_data
@@ -88,7 +87,7 @@ class DatoController():
repeticion=F("id_dato__id_calificacion__num_repeticion"),
producto_code=F(
"id_dato__id_calificacion__id_producto__codigoProducto"),
- usuarioCatador=F(
+ usuario_catador=F(
"id_dato__id_calificacion__id_catador__user__username"),
dato_valor=F("valor")
)
diff --git a/tecnicas/controllers/models_controller/palabras_controller.py b/tecnicas/controllers/models_controller/palabras_controller.py
index 3aafd619ecd57fe10ca3746a4b49257f2ba3bd92..2a336b31b6f44594637d7b960dc39ee8a0bd419e 100644
--- a/tecnicas/controllers/models_controller/palabras_controller.py
+++ b/tecnicas/controllers/models_controller/palabras_controller.py
@@ -22,17 +22,16 @@ class PalabrasController():
@staticmethod
def getWordsInTechnique(technique: Tecnica):
if technique.id_estilo.nombre_estilo == "atributos":
- es_atribute = EsAtributo.objects.get(id_tecnica=technique)
- words = list(es_atribute.palabras.all())
+ words = list(technique.tecnica_esatributo.palabras.all())
+ if not words:
+ return controller_error("Técnica sin palabras")
return words
elif technique.id_estilo.nombre_estilo == "vocabulario":
- try:
- palabras = Palabra.objects.filter(
- vocabulario__esvocabulario__id_tecnica=technique
- )
- return list(palabras.distinct()) if palabras.exists() else controller_error("Técnica sin palabras con vocabulario")
- except Exception as e:
+ words = list(
+ technique.tecnica_esvacabulario.id_vocabulario.palabras.all())
+ if not words:
return controller_error("Técnica sin palabras con vocabulario")
+ return words
@staticmethod
def getWordsWithoutData(recoreded_data: list[Dato], words: list[Palabra]):
diff --git a/tecnicas/controllers/models_controller/particiapacion_controller.py b/tecnicas/controllers/models_controller/particiapacion_controller.py
index bd0448b1ee796fe063fbe69d36ffaed8b2df65a9..a902b0dbe9c9da75248eae1280820f32a1b72b22 100644
--- a/tecnicas/controllers/models_controller/particiapacion_controller.py
+++ b/tecnicas/controllers/models_controller/particiapacion_controller.py
@@ -16,15 +16,12 @@ class ParticipacionController():
return controller_error("No se ha encontrado la participación")
@staticmethod
- def finishSession(id_participation: int):
- try:
- participation = Participacion.objects.get(id=id_participation)
- participation.finalizado = True
- participation.activo = False
- participation.save()
- return participation
- except Participacion.DoesNotExist:
- return controller_error("No se ha encontrado la participación")
+ def finishSession(participation: Participacion):
+ participation.refresh_from_db()
+ participation.finalizado = True
+ participation.activo = False
+ participation.save()
+ return participation
@staticmethod
def outSession(tester: Catador, session: SesionSensorial):
diff --git a/tecnicas/controllers/models_controller/sesion_controller.py b/tecnicas/controllers/models_controller/sesion_controller.py
index 0ff4c82a05ca7d5421d5e24ac3788d2b14fefc8e..0c5f7547577cb157cbd8b266c394d6fd1355919b 100644
--- a/tecnicas/controllers/models_controller/sesion_controller.py
+++ b/tecnicas/controllers/models_controller/sesion_controller.py
@@ -131,14 +131,6 @@ class SesionController():
else:
use_session = session
- (is_update_participations,
- message) = ParticipacionController.outAllInSession(use_session)
-
- if not is_update_participations:
- return controller_error(message)
-
use_session.activo = False
-
use_session.save()
-
return session
diff --git a/tecnicas/controllers/views_controller/api_rating_controller.py b/tecnicas/controllers/views_controller/api_rating_controller.py
index 19100b4d97810f0f40c333f4f3c9199f02609d73..e65014b2c618238105db13e494fb5c7dcbbc52d2 100644
--- a/tecnicas/controllers/views_controller/api_rating_controller.py
+++ b/tecnicas/controllers/views_controller/api_rating_controller.py
@@ -7,65 +7,18 @@ class ApiRatingController():
self.rating_controller = rating_controller
self.data_controller = data_controller
- def setRating(self) -> int | dict:
- repetition = self.rating_controller.setRepetition()
- if isinstance(repetition, dict):
- return controller_error(repetition["error"])
- return repetition
+ def controllPostScales(self) -> dict:
+ self.data_controller.setRating(
+ new_rating=self.rating_controller.rating
+ )
- def saveRating(self):
- rating = self.rating_controller.saveRating()
- if isinstance(rating, dict):
- return controller_error(rating["error"])
- return rating
-
- def setRatingInData(self):
- update_rating = self.data_controller.setRating(
- self.rating_controller.rating)
- if isinstance(update_rating, dict):
- return controller_error(update_rating["error"])
- return update_rating
-
- def saveData(self):
data = self.data_controller.saveData()
- if isinstance(data, dict):
- return controller_error(data["error"])
- return data
-
- def setValueRating(self):
- self.data_controller.setInstanceValue()
-
- def saveValue(self):
- value = self.data_controller.saveValue()
- if isinstance(value, dict):
- return controller_error(value["error"])
-
- def logicView(self) -> dict:
- validate = self.rating_controller.validateRating()
- if isinstance(validate, dict):
- return controller_error(validate["error"])
-
- reptition = self.setRating()
- if isinstance(reptition, dict):
- return controller_error(reptition["error"])
-
- rating = self.saveRating()
- if isinstance(rating, dict):
- return controller_error(rating["error"])
-
- rating_data = self.setRatingInData()
- if isinstance(rating_data, dict):
- return controller_error(rating_data["error"])
-
- data = self.saveData()
if isinstance(data, dict):
return controller_error(data["error"])
- value = self.setValueRating()
- if isinstance(value, dict):
- return controller_error(value["error"])
+ self.data_controller.setValue()
- value_save = self.saveValue()
+ value_save = self.data_controller.saveValue()
if isinstance(value_save, dict):
return controller_error(value_save["error"])
diff --git a/tecnicas/controllers/views_controller/create_session/panel_basic_controller.py b/tecnicas/controllers/views_controller/create_session/panel_basic_controller.py
index 7afd63508db47c0be43062860ac2fd9cfcec08da..b2fc94263a354656a6ede862fb5d32151f752f1f 100644
--- a/tecnicas/controllers/views_controller/create_session/panel_basic_controller.py
+++ b/tecnicas/controllers/views_controller/create_session/panel_basic_controller.py
@@ -1,4 +1,4 @@
-from tecnicas.forms import SesionBasicForm
+from tecnicas.forms import SesionBasicForm, SesionBasicCATAForm
from django.http import HttpRequest
from django.shortcuts import redirect, render
from django.urls import reverse
@@ -10,6 +10,14 @@ class PanelBasicController():
"numero_repeticiones": 1
}
+ url_panel_basic = "tecnicas/create_sesion/configuracion-panel-basic.html"
+ url_panel_basic_cata = "tecnicas/create_sesion/panel-basic-cata.html"
+
+ url_next_panel_scales = "cata_system:panel_configuracion_tags"
+ url_next_panel_cata = "cata_system:panel_configuracion_codes"
+
+ url_select_technique = "cata_system:seleccion_tecnica"
+
def __init__(self):
pass
@@ -23,7 +31,7 @@ class PanelBasicController():
}
response = render(
- request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
+ request, PanelBasicController.url_panel_basic, view_context)
return response
@staticmethod
@@ -44,11 +52,11 @@ class PanelBasicController():
response = redirect(
reverse("cata_system:panel_configuracion_tags"))
else:
- response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
+ response = render(request, PanelBasicController.url_panel_basic, {
"form_sesion": form, "error": "Información no valida"})
except KeyError:
response = redirect(reverse(
- "cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
+ PanelBasicController.url_select_technique) + "?error=error en datos de configuracion")
return response
@@ -63,7 +71,7 @@ class PanelBasicController():
}
response = render(
- request, "tecnicas/create_sesion/configuracion-panel-basic.html", view_context)
+ request, PanelBasicController.url_panel_basic, view_context)
return response
@staticmethod
@@ -88,7 +96,7 @@ class PanelBasicController():
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", {
+ response = render(request, PanelBasicController.url_panel_basic, {
"form_sesion": form, "error": "No puedes modificar el número de catadores o repeticiones"})
else:
values["name_tecnica"] = name_tecnica
@@ -96,10 +104,44 @@ class PanelBasicController():
response = redirect(
reverse("cata_system:panel_configuracion_tags"))
else:
- response = render(request, "tecnicas/create_sesion/configuracion-panel-basic.html", {
+ response = render(request, PanelBasicController.url_panel_basic, {
"form_sesion": form, "error": "Información no valida"})
except KeyError:
response = redirect(reverse(
- "cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
+ PanelBasicController.url_select_technique) + "?error=error en datos de configuracion")
+
+ return response
+
+ @staticmethod
+ def controllGetCATA(request: HttpRequest):
+ form_sesion = SesionBasicCATAForm()
+
+ view_context = {
+ "form_sesion": form_sesion,
+ "use_technique": "cata"
+ }
+
+ return render(
+ request, PanelBasicController.url_panel_basic_cata, view_context)
+
+ @staticmethod
+ def controllPostCATA(request: HttpRequest, name_tecnica: str):
+ form = SesionBasicCATAForm(request.POST)
+
+ if form.is_valid():
+ values = {}
+ for name, value in form.cleaned_data.items():
+ if name == "estilo_palabras":
+ values[name] = value.id
+ else:
+ values[name] = value
+
+ values["name_tecnica"] = name_tecnica
+ request.session['form_basic'] = values
+ response = redirect(
+ reverse(PanelBasicController.url_next_panel_cata))
+ else:
+ response = render(request, PanelBasicController.url_panel_basic, {
+ "form_sesion": form, "error": "Información no valida"})
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
index 9bef632d61abfa2d6e72519ddb24780b2d3f430e..96ac24f4636d9b26291f8fe004f279b48aae6ab6 100644
--- a/tecnicas/controllers/views_controller/create_session/panel_codes_controller.py
+++ b/tecnicas/controllers/views_controller/create_session/panel_codes_controller.py
@@ -7,15 +7,16 @@ import json
class PanelCodesController():
+ url_current_panel = "tecnicas/create_sesion/configuracion-panel-codes.html"
+ url_next_panel = "cata_system:panel_configuracion_words"
+
def __init__(self):
pass
@staticmethod
def controllGetEscalas(request: HttpRequest, data):
- (
- num_products,
- num_tester
- ) = PanelCodesController.defineInfoConvencional(data)
+ num_products = data["numero_productos"]
+ num_tester = data["numero_catadores"]
codes_products = generarCodigos(num_products)
@@ -27,14 +28,11 @@ class PanelCodesController():
"use_technique": "escalas"
}
- return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
@staticmethod
def controllPostEscalas(request: HttpRequest, data):
- (
- num_products,
- num_tester
- ) = PanelCodesController.defineInfoConvencional(data)
+ num_tester = data["numero_catadores"]
sorts_code = json.loads(request.POST.get("sort_codes"))
codes = []
@@ -60,11 +58,11 @@ class PanelCodesController():
codes_sort["sort_codes"] = sorts_code
request.session["form_codes"] = codes_sort
- return redirect(reverse("cata_system:panel_configuracion_words"))
+ return redirect(reverse(PanelCodesController.url_next_panel))
else:
context_codes_form["error"] = "error en los datos recibidos"
- return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
@staticmethod
def controllGetRATA(request: HttpRequest, data):
@@ -78,10 +76,10 @@ class PanelCodesController():
"use_technique": "rata"
}
- return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
@staticmethod
- def controllPostRATA(request: HttpRequest):
+ def controllPostRATA(request: HttpRequest, is_rata: True):
codes = []
context_codes_form = {}
@@ -93,22 +91,26 @@ class PanelCodesController():
context_codes_form = {
"form_codes": form_codes,
- "use_technique": "rata"
+ "use_technique": "rata" if is_rata else "cata"
}
if form_codes.is_valid():
request.session["form_codes"] = codes
- return redirect(reverse("cata_system:panel_configuracion_words"))
+ return redirect(reverse(PanelCodesController.url_next_panel))
else:
context_codes_form["error"] = "error en los datos recibidos"
- return render(request, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
@staticmethod
- def defineInfoConvencional(data):
+ def controllGetCATA(request: HttpRequest, data):
num_products = data["numero_productos"]
- num_tester = data["numero_catadores"]
- return (
- num_products,
- num_tester
- )
+ codes_products = generarCodigos(num_products)
+ form_codes = CodesForm(codes=codes_products)
+
+ context_codes_form = {
+ "form_codes": form_codes,
+ "use_technique": "cata"
+ }
+
+ return render(request, PanelCodesController.url_current_panel, context_codes_form)
diff --git a/tecnicas/controllers/views_controller/create_session/panel_create_controller.py b/tecnicas/controllers/views_controller/create_session/panel_create_controller.py
index 42a3b79c3431966cf333137549af310f92dab72e..dbab342d25a99d33a73c48615208300b7bdfcd51 100644
--- a/tecnicas/controllers/views_controller/create_session/panel_create_controller.py
+++ b/tecnicas/controllers/views_controller/create_session/panel_create_controller.py
@@ -2,6 +2,7 @@ from django.http import HttpRequest, JsonResponse
from django.db import transaction
from django.shortcuts import render
from tecnicas.utils import general_error
+from tecnicas.models import EsAtributo, EsVocabulario, Vocabulario, Tecnica, TipoTecnica, EstiloPalabra, Producto, Palabra, SesionSensorial
from tecnicas.controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, EstiloPalabrasController, PalabrasController, SesionController
from tecnicas.utils import deleteDataSession
@@ -21,139 +22,155 @@ class PanelCreateController():
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")
+ try:
+ 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:
+ raise ValueError("Error al guardar la técnica")
+
+ data_scale = {
+ "id_scale": data_basic["tipo_escala"],
+ "size": data_basic["tamano_escala"],
+ "technique": technique
+ }
- 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
+ controllerScale = EscalaController(data=data_scale)
+
+ scale = controllerScale.saveScale()
+ if isinstance(scale, dict):
+ raise ValueError(scale["error"])
+
+ dict_tags = request.session["form_tags"]
+ saved_related_tags = controllerScale.realteTags(dict_tags)
+ if "error" in saved_related_tags:
+ raise ValueError(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):
+ raise ValueError(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):
+ raise ValueError(saved_orders["error"])
+
+ seded_positions = controllerOrdes.setPositions()
+ if isinstance(seded_positions, dict):
+ raise ValueError(seded_positions["error"])
+
+ saved_postions = controllerOrdes.savePositions()
+ if isinstance(saved_postions, dict):
+ raise ValueError(saved_prodcuts["error"])
+
+ # /////////////////////////////////////////////////////// #
+ #
+ # Third step: Create relations technique with Words Style #
+ #
+ # /////////////////////////////////////////////////////// #
+ style_words = technique.id_estilo.nombre_estilo
+ if style_words == "atributos":
+ ids_words = request.session["form_words"]
+ words_controller = PalabrasController(ids=ids_words)
+
+ words_to_use = words_controller.setWords()
+ if isinstance(words_to_use, dict):
+ raise ValueError(words_to_use["error"])
+
+ style_controller = EstiloPalabrasController(
+ technique=technique, words=words_to_use)
+
+ instace_style = style_controller.createAndSaveInstaceStyle()
+ if isinstance(instace_style, dict):
+ raise ValueError(instace_style["error"])
+
+ words_using = style_controller.relatedWords()
+ if isinstance(words_using, dict):
+ raise ValueError(words_using["error"])
+ elif style_words == "vocabulario":
+ name_vocabulary = request.session["form_words"]
+ vocabulary = Vocabulario.objects.get(
+ nombre_vocabulario=name_vocabulary)
+
+ es_vocabulary = EsVocabulario.objects.create(
+ id_tecnica=technique,
+ id_vocabulario=vocabulary
+ )
+ else:
+ raise ValueError("Estilo de palabas no permitido")
+
+ # //////////////////////////////////////////////////////// #
+ #
+ # 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):
+ raise ValueError(setting_session["error"])
+
+ saved_session = session_controller.saveSession()
+ if isinstance(saved_session, dict):
+ raise ValueError(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 #
- #
- # ////////////////////////////////// #
+ # ////////////////////////////////// #
+ #
+ # Final step: Delete date in session #
+ #
+ # ////////////////////////////////// #
- deleteDataSession(request)
- return JsonResponse(context)
+ deleteDataSession(request)
+ return JsonResponse(context)
+ except ValueError as e:
+ return general_error(f"Error: {e}")
else:
return general_error("No se ha establecido acción")
@@ -163,112 +180,267 @@ class PanelCreateController():
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")
+ try:
+ 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
+
+ technique = Tecnica.objects.create(
+ tipo_tecnica=TipoTecnica.objects.get(
+ nombre_tecnica=data_basic["name_tecnica"]),
+ id_estilo=EstiloPalabra.objects.get(
+ id=data_basic["estilo_palabras"]),
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
+ limite_catadores=data_basic["numero_catadores"],
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
+ )
+
+ if not technique:
+ raise ValueError("Error al guardar la técnica")
+
+ data_scale = {
+ "id_scale": data_basic["tipo_escala"],
+ "size": data_basic["tamano_escala"],
+ "technique": technique
+ }
- 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
+ controllerScale = EscalaController(data=data_scale)
+
+ scale = controllerScale.saveScale()
+ if isinstance(scale, dict):
+ raise ValueError(scale["error"])
+
+ dict_tags = request.session["form_tags"]
+ saved_related_tags = controllerScale.realteTags(dict_tags)
+ if "error" in saved_related_tags:
+ raise ValueError(saved_related_tags["error"])
+
+ # ////////////////////////////////////////////// #
+ #
+ # Second step: Create productos with their codes #
+ #
+ # ////////////////////////////////////////////// #
+ codes = request.session["form_codes"]
+
+ if not codes:
+ raise ValueError("No hay códigos de productos")
+
+ products_without_save = []
+ for code in codes:
+ product = Producto(
+ codigoProducto=code,
+ id_tecnica=technique
+ )
+ products_without_save.append(product)
+
+ Producto.objects.bulk_create(products_without_save)
+
+ # /////////////////////////////////////////////////////// #
+ #
+ # Third step: Create relations technique with Words Style #
+ #
+ # /////////////////////////////////////////////////////// #
+ style_words = technique.id_estilo.nombre_estilo
+
+ if style_words == "atributos":
+ raw_ids_words = request.session["form_words"]
+ ids_words = [int(id_w) for id_w in raw_ids_words]
+
+ words = Palabra.objects.filter(id__in=ids_words)
+
+ style_atribute = EsAtributo.objects.create(
+ id_tecnica=technique
+ )
+
+ if not style_atribute:
+ raise ValueError(
+ "Error al intentar relacionar las palabras con la técnica")
+
+ style_atribute.palabras.set(words)
+
+ elif style_words == "vocabulario":
+ name_vocabulary = request.session["form_words"]
+ try:
+ vocabulary = Vocabulario.objects.get(
+ nombre_vocabulario=name_vocabulary)
+ except Vocabulario.DoesNotExist:
+ raise ValueError("Vocabulario no encontrado")
+
+ es_vocabulary = EsVocabulario.objects.create(
+ id_tecnica=technique,
+ id_vocabulario=vocabulary
+ )
+ if not es_vocabulary:
+ raise ValueError(
+ "Error al intentar relacionar el vocabulario con la técnica")
+
+ else:
+ raise ValueError("Estilo de palabas no permitido")
+
+ # //////////////////////////////////////////////////////// #
+ #
+ # Fourth step: Create session and relat with the technique #
+ #
+ # //////////////////////////////////////////////////////// #
+ session = SesionSensorial.objects.create(
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
+ technique=technique,
+ creator=request.user.user_presentador
+ )
+
+ if not session:
+ raise ValueError("Error al crear sesion sensorial")
+
+ context = {
+ "message": "sesión creada",
+ "data": {
+ "codigo_sesion": session.codigo_sesion,
+ "nombre_sesion": session.nombre_sesion
+ }
}
- }
- # ////////////////////////////////// #
- #
- # Final step: Delete date en session #
- #
- # ////////////////////////////////// #
+ # ////////////////////////////////// #
+ #
+ # Final step: Delete date en session #
+ #
+ # ////////////////////////////////// #
+
+ deleteDataSession(request)
+ return JsonResponse(context)
+ except ValueError as e:
+ return general_error(f"Error: {e}")
+ else:
+ return general_error("No se ha establecido acción")
+
+ @staticmethod
+ def controllPostCATA(request: HttpRequest):
+ if request.POST.get('action') == 'create_session':
+ if not request.session.get("form_codes") or not request.session.get("form_words"):
deleteDataSession(request)
- return JsonResponse(context)
+ return general_error("No se ha especificado información necesaria para la creación de la sesión, por favor, vuelve a intentarlo")
+ try:
+ with transaction.atomic():
+ # //////////////////////////// #
+ #
+ # First step: Create technique #
+ #
+ # //////////////////////////// #
+ data_basic = request.session["form_basic"]
+ data_basic["numero_catadores"] = 0
+ data_basic["numero_repeticiones"] = 1
+
+ technique = Tecnica.objects.create(
+ tipo_tecnica=TipoTecnica.objects.get(
+ nombre_tecnica=data_basic["name_tecnica"]),
+ id_estilo=EstiloPalabra.objects.get(
+ id=data_basic["estilo_palabras"]),
+ repeticiones_max=data_basic["numero_repeticiones"] or 1,
+ limite_catadores=data_basic["numero_catadores"],
+ instrucciones=data_basic["instrucciones"] or "Espere instrucciones del Presentador",
+ )
+
+ if not technique:
+ raise ValueError("Error al guardar la técnica")
+
+ # ////////////////////////////////////////////// #
+ #
+ # Second step: Create productos with their codes #
+ #
+ # ////////////////////////////////////////////// #
+ codes = request.session["form_codes"]
+
+ if not codes:
+ raise ValueError("No hay códigos de productos")
+
+ products_without_save = []
+ for code in codes:
+ product = Producto(
+ codigoProducto=code,
+ id_tecnica=technique
+ )
+ products_without_save.append(product)
+
+ Producto.objects.bulk_create(products_without_save)
+
+ # /////////////////////////////////////////////////////// #
+ #
+ # Third step: Create relations technique with Words Style #
+ #
+ # /////////////////////////////////////////////////////// #
+ style_words = technique.id_estilo.nombre_estilo
+
+ if style_words == "atributos":
+ raw_ids_words = request.session["form_words"]
+ ids_words = [int(id_w) for id_w in raw_ids_words]
+
+ words = Palabra.objects.filter(id__in=ids_words)
+
+ style_atribute = EsAtributo.objects.create(
+ id_tecnica=technique
+ )
+
+ if not style_atribute:
+ raise ValueError(
+ "Error al intentar relacionar las palabras con la técnica")
+
+ style_atribute.palabras.set(words)
+
+ elif style_words == "vocabulario":
+ name_vocabulary = request.session["form_words"]
+ try:
+ vocabulary = Vocabulario.objects.get(
+ nombre_vocabulario=name_vocabulary)
+ except Vocabulario.DoesNotExist:
+ raise ValueError("Vocabulario no encontrado")
+
+ es_vocabulary = EsVocabulario.objects.create(
+ id_tecnica=technique,
+ id_vocabulario=vocabulary
+ )
+ if not es_vocabulary:
+ raise ValueError(
+ "Error al intentar relacionar el vocabulario con la técnica")
+
+ else:
+ raise ValueError("Estilo de palabas no permitido")
+
+ # //////////////////////////////////////////////////////// #
+ #
+ # Fourth step: Create session and relat with the technique #
+ #
+ # //////////////////////////////////////////////////////// #
+ session = SesionSensorial.objects.create(
+ nombre_sesion=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
+ tecnica=technique,
+ creadoPor=request.user.user_presentador
+ )
+
+ if not session:
+ raise ValueError("Error al crear sesion sensorial")
+
+ context = {
+ "message": "sesión creada",
+ "data": {
+ "codigo_sesion": session.codigo_sesion,
+ "nombre_sesion": session.nombre_sesion
+ }
+ }
+
+ # ////////////////////////////////// #
+ #
+ # Final step: Delete date en session #
+ #
+ # ////////////////////////////////// #
+ deleteDataSession(request)
+ return JsonResponse(context)
+
+ except ValueError as e:
+ return general_error(f"Error: {e}")
else:
return general_error("No se ha establecido acción")
diff --git a/tecnicas/controllers/views_controller/create_session/panel_words_controller.py b/tecnicas/controllers/views_controller/create_session/panel_words_controller.py
index 138897bfc993e5de157192468123479cbf5b32a6..92d454c44096a43608ec5fbe936f3d873e99d5ff 100644
--- a/tecnicas/controllers/views_controller/create_session/panel_words_controller.py
+++ b/tecnicas/controllers/views_controller/create_session/panel_words_controller.py
@@ -1,5 +1,5 @@
from django.http import HttpRequest
-from tecnicas.forms import WordForm
+from tecnicas.forms import WordForm, VocabularioSelectForm
from django.shortcuts import render, redirect
from django.urls import reverse
from tecnicas.models import Palabra
@@ -7,27 +7,36 @@ import json
class PanelWordsController():
+ current_url_escalas_atribute = "tecnicas/create_sesion/configuracion-panel-words.html"
+ current_url_escalas_vocabulary = "tecnicas/create_sesion/conf-panel-vocabulary.html"
+
def __init__(self):
pass
@staticmethod
- def controllGetEscalas(request: HttpRequest):
+ def controllGetEscalasAtributes(request: HttpRequest):
form = WordForm()
context = {
"form_word": form
}
- return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
+ return render(request, PanelWordsController.current_url_escalas_atribute, context)
+
+ @staticmethod
+ def controllGetEscalasVocabulary(request: HttpRequest):
+ form = VocabularioSelectForm()
+ context = {"form": form}
+ return render(request, PanelWordsController.current_url_escalas_vocabulary, context)
@staticmethod
- def controllPostEscalas(request: HttpRequest):
+ def controllPostEscalasAtributes(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)
+ return render(request, PanelWordsController.current_url_escalas_atribute, context)
words = json.loads(request.POST.get("words"))
context["words"] = words
@@ -36,14 +45,34 @@ class PanelWordsController():
if len(ids_words) != len(set(ids_words)):
context["error"] = "existen palabras duplicadas"
- return render(request, "tecnicas/create_sesion/configuracion-panel-words.html", context)
+ return render(request, PanelWordsController.current_url_escalas_atribute, 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)
+ return render(request, PanelWordsController.current_url_escalas_atribute, context)
request.session["form_words"] = ids_words
return redirect(reverse("cata_system:creando_sesion"))
+
+ @staticmethod
+ def controllPostEscalasVocabulary(request: HttpRequest):
+ context = {}
+ if not request.POST.get("vocabulario"):
+ context["form"] = VocabularioSelectForm()
+ context["error"] = "No hay un vocabulario seleccionado"
+ return render(request, PanelWordsController.current_url_escalas_vocabulary, context)
+
+ form = VocabularioSelectForm(request.POST)
+ vocabulary: int
+ if form.is_valid():
+ vocabulary = form.cleaned_data["vocabulario"]
+ else:
+ context["form"] = VocabularioSelectForm()
+ context["error"] = "Erro al validar el vocabulario"
+ return render(request, PanelWordsController.current_url_escalas_vocabulary, context)
+
+ request.session["form_words"] = vocabulary.nombre_vocabulario
+ return redirect(reverse("cata_system:creando_sesion"))
diff --git a/tecnicas/controllers/views_controller/main_tester_form_controller.py b/tecnicas/controllers/views_controller/main_tester_form_controller.py
deleted file mode 100644
index f35d2fc57bed07f5d880476303944552fa59587d..0000000000000000000000000000000000000000
--- a/tecnicas/controllers/views_controller/main_tester_form_controller.py
+++ /dev/null
@@ -1,84 +0,0 @@
-from tecnicas.models import Catador, SesionSensorial, Orden, Participacion, Producto, EsAtributo, Calificacion, EsVocabulario
-from ...utils import controller_error, shuffleArray
-from django.db import transaction
-
-
-class MainTesterFormController():
- tester: Catador
- session: SesionSensorial
- order: Orden | dict
-
- def __init__(self, code_session: str, user_tester: str):
- try:
- self.tester = Catador.objects.get(user__username=user_tester)
- self.session = SesionSensorial.objects.get(
- codigo_sesion=code_session)
- except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
- return controller_error("Parámetros inexistentes")
-
- def assignOrder(self):
- with transaction.atomic():
- 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")
-
- shuffle_orders = shuffleArray(orders_without_tester)
- self.order_to_assign = shuffle_orders.pop()
-
- self.order_to_assign.id_catador = self.tester
- self.order_to_assign.save()
-
- return self.order_to_assign
-
- def checkAndAssignOrder(self):
- try:
- self.order_to_assign = Orden.objects.get(
- id_tecnica=self.session.tecnica, id_catador=self.tester)
- except Orden.DoesNotExist:
- create = self.assignOrder()
- if isinstance(create, dict):
- return create
- return self.order_to_assign
-
- def isEndedSession(self, repetition: int):
- try:
- participation = Participacion.objects.get(
- catador=self.tester, tecnica=self.session.tecnica)
-
- # ////////////////////////////////////////////////////////////// #
- #
- # Si numero_calificaciones_esperadas = num_productos * num_palabras
- # Es igual a numero_calificaciones_actuales en la repetcion R
- # Ha terminado la repeticion
- #
- # ////////////////////////////////////////////////////////////// #
-
- if participation.finalizado:
- 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()
-
- num_ratings_now = Calificacion.objects.filter(
- id_tecnica=self.session.tecnica, id_catador=self.tester, num_repeticion=repetition).count()
-
- expected_ratings_repetition = num_products * num_words
-
- return num_ratings_now >= expected_ratings_repetition
- else:
- return participation.finalizado
- except Participacion.DoesNotExist:
- return controller_error("No se ha encontrado la participación")
diff --git a/tecnicas/controllers/views_controller/session_management/details_escala_controller.py b/tecnicas/controllers/views_controller/session_management/details_escala_controller.py
index 9580e3cd41c7058944b9925611789aa0f6255c5e..a36c8080fa544fee777efeaccb516e4e0b49ea4e 100644
--- a/tecnicas/controllers/views_controller/session_management/details_escala_controller.py
+++ b/tecnicas/controllers/views_controller/session_management/details_escala_controller.py
@@ -14,8 +14,8 @@ Encabezados de como deben de aparecer los datos juntos
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 tecnicas.models import SesionSensorial, Presentador, Participacion, Calificacion, Escala
+from tecnicas.controllers import DatoController, PalabrasController, ParticipacionController
from .details_controller import DetallesController
from tecnicas.utils import defaultdict_to_dict, controller_error
from collections import defaultdict
@@ -34,7 +34,7 @@ class DetallesEscalasController(DetallesController):
context["error"] = error
if message != "" or message:
context["message"] = message
-
+
return render(
request, self.url_template, context)
@@ -43,28 +43,41 @@ class DetallesEscalasController(DetallesController):
"use_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
}
self.context["sesion"] = self.session
+
+ technique = self.session.tecnica
+
+ # Datos de la escala usada
+ scale: Escala = technique.escala_tecnica
+
+ self.context["scale"] = {
+ "type": scale.id_tipo_escala.nombre_escala,
+ "size": scale.longitud
+ }
+
+ # Recuperar la palabras de la tecnica
self.words = PalabrasController.getWordsInTechnique(
- self.session.tecnica)
+ technique)
self.context["palabras"] = [word.nombre_palabra for word in self.words]
+ # Se recuperan las calificaciones
ratings_for_repetition = []
- ratings = CalificacionController.getRatingsByTechnique(
- technique=self.session.tecnica)
+ ratings = list(Calificacion.objects.filter(
+ id_tecnica=technique))
- if isinstance(ratings, dict) or not ratings:
+ if 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=ratings, technique=technique)
ratings_for_repetition = defaultdict(
lambda: defaultdict(lambda: defaultdict(list)))
for item in data:
- user = item["usuarioCatador"]
+ user = item["usuario_catador"]
rep = item["repeticion"]
prod = item["producto_code"]
@@ -77,18 +90,30 @@ class DetallesEscalasController(DetallesController):
ratings_for_repetition)
self.context["existen_calificaciones"] = True
+ # Se comprueba que ya no se pueda iniciar la repeticion
+ self.context["fin_repeticiones"] = technique.repeticion >= technique.repeticiones_max
+
return self.context
- def startRepetition(self, presenter: Presentador):
+ def startRepetition(self, presenter: Presentador, request: HttpRequest):
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")
+ return self.getResponse(error="Solo el presentador que crea la sesión puede iniciar la repetición", request=request)
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")
+ return self.getResponse(error="La sesión ya está activada", request=request)
+ elif technique.repeticion >= technique.repeticiones_max:
+ return self.getResponse(error="Se ha alcanzado el número de repeticiones máxima", request=request)
+
+ there_participacions = Participacion.objects.filter(
+ tecnica=technique).exists()
+
+ if there_participacions:
+ (is_update_participations,
+ message) = ParticipacionController.outAllInSession(self.session)
+ if not is_update_participations:
+ return self.getResponse(error=message, request=request)
self.session.activo = True
technique.repeticion = technique.repeticion + 1
diff --git a/tecnicas/controllers/views_controller/session_management/monitor_controller.py b/tecnicas/controllers/views_controller/session_management/monitor_controller.py
index cf0dacad71a06db7a9c1d94deef1e9c7b79ec5fd..b0f2a19b1845b605f1bd174399e34b300e7da89b 100644
--- a/tecnicas/controllers/views_controller/session_management/monitor_controller.py
+++ b/tecnicas/controllers/views_controller/session_management/monitor_controller.py
@@ -1,10 +1,59 @@
-from tecnicas.models import SesionSensorial
+from django.http import HttpRequest
+from django.shortcuts import render
+from tecnicas.models import SesionSensorial, Producto, EsAtributo, EsVocabulario
+from tecnicas.controllers import ParticipacionController, SesionController
+from tecnicas.utils import controller_error
class MonitorController():
+ url_view: str
+ previus_view: str
+
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)
+ 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 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 getExpectedRatingsEscalasRapida(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 finishSession(self):
+ response = SesionController.finishRepetion(self.sensorial_session)
+ if isinstance(response, dict):
+ return controller_error(response["error"])
+ self.sensorial_session.refresh_from_db()
+ return self.sensorial_session
diff --git a/tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py b/tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py
index eeb47150440a00bc262f7c7e4cb5b4b1414cacf6..36b976bf9cc8038f71021cbdbdfc6413bf436fab 100644
--- a/tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py
+++ b/tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py
@@ -1,93 +1,49 @@
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 tecnicas.models import Dato, Participacion
+from tecnicas.controllers import SesionController
from .monitor_controller import MonitorController
class MonitorEscalasController(MonitorController):
- url_view = "tecnicas/manage_sesions/monitor-sesion.html"
-
def __init__(self, session: SesionController):
super().__init__(session)
+ self.url_view = "tecnicas/manage_sesions/monitor-sesion.html"
+ self.previus_view = "cata_system:detalles_sesion"
- 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):
+ def controllPostFinishSession(self, request: HttpRequest):
self.setContext()
- (is_all_end, message) = self.checkAllParticipantsEnded()
+ (is_all_end, message) = self.checkAllFinish()
if not is_all_end:
self.context["error"] = message
- return render(request, "tecnicas/manage_sesions/monitor-sesion.html", self.context)
+ return render(request, self.url_view, 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)
+ return render(request, self.url_view, 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)
+ return redirect(reverse(self.previus_view, kwargs={"session_code": self.sensorial_session.codigo_sesion}))
- 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):
+ def checkAllFinish(self):
technique = self.sensorial_session.tecnica
- expected_ratings_repetition = self.getExpectedRatings()
+ expected_ratings_repetition = self.getExpectedRatingsEscalasRapida()
- all_participations = ParticipacionController.getParticipationsInTechinique(
- technique=technique)
+ all_participations = list(
+ Participacion.objects.filter(tecnica=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()
+ num_ratings_now = Dato.objects.filter(
+ id_calificacion__num_repeticion=technique.repeticion,
+ id_calificacion__id_catador=particiapation.catador,
+ id_calificacion__id_tecnica=technique
+ ).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/session_management/monitor_rata_controller.py b/tecnicas/controllers/views_controller/session_management/monitor_rata_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..3337848fe4bc36561fc5968e68c71a5e71f08a28
--- /dev/null
+++ b/tecnicas/controllers/views_controller/session_management/monitor_rata_controller.py
@@ -0,0 +1,46 @@
+from django.http import HttpRequest
+from django.shortcuts import render, redirect
+from django.urls import reverse
+from tecnicas.models import Dato, Participacion
+from tecnicas.controllers import SesionController
+from .monitor_controller import MonitorController
+
+
+class MonitorRATAController(MonitorController):
+ def __init__(self, session: SesionController):
+ super().__init__(session)
+ self.url_view = "tecnicas/manage_sesions/monitor-sesion.html"
+ self.previus_view = "cata_system:detalles_sesion"
+
+ def controllPostFinishSession(self, request: HttpRequest):
+ self.setContext()
+ (is_all_end, message) = self.checkAllFinish()
+ if not is_all_end:
+ self.context["error"] = message
+ return render(request, self.url_view, self.context)
+ response = self.finishSession()
+ if isinstance(response, dict):
+ self.context["error"] = response["error"]
+ return render(request, self.url_view, self.context)
+ self.context["message"] = message
+ return redirect(reverse(self.previus_view, kwargs={"session_code": self.sensorial_session.codigo_sesion}))
+
+ def checkAllFinish(self):
+ technique = self.sensorial_session.tecnica
+
+ expected_ratings_repetition = self.getExpectedRatingsEscalasRapida()
+
+ all_participations = list(
+ Participacion.objects.filter(tecnica=technique))
+
+ for particiapation in all_participations:
+ num_ratings_now = Dato.objects.filter(
+ id_calificacion__num_repeticion=technique.repeticion,
+ id_calificacion__id_catador=particiapation.catador,
+ id_calificacion__id_tecnica=technique
+ ).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")
diff --git a/tecnicas/controllers/views_controller/sessions_tester/convencional_scales_controller.py b/tecnicas/controllers/views_controller/sessions_tester/convencional_scales_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..897fa25cd4bade5a006e973de0818f5e4fbba06d
--- /dev/null
+++ b/tecnicas/controllers/views_controller/sessions_tester/convencional_scales_controller.py
@@ -0,0 +1,158 @@
+from django.http import HttpRequest
+from django.shortcuts import redirect, render
+from django.urls import reverse
+from tecnicas.models import SesionSensorial, Catador, Participacion, Producto, Calificacion, Palabra
+from tecnicas.controllers import PosicionController, CalificacionController, ParticipacionController, PalabrasController, EscalaController, DatoController
+
+
+class ConvencionalScalesController:
+ context = {}
+ current_directory = "tecnicas/forms_tester/convencional.html"
+ previus_directory = "cata_system:catador_init_session"
+
+ def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
+ self.tester = user_tester
+ self.session = sensorial_session
+
+ def controllGetEscalas(self, request: HttpRequest):
+ technique = self.session.tecnica
+ self.participation = Participacion.objects.get(
+ tecnica=technique, catador=request.user.user_catador)
+
+ ctx = self.context
+ ctx["session"] = self.session
+
+ # Obtener posiciones y palabras de la técnica
+ positions_in_order = PosicionController.getPostionsInOrder(
+ id_order=request.session["id_order"])
+ aligned_positions_in_order = sorted(
+ positions_in_order, key=lambda p: p.posicion)
+ words = PalabrasController.getWordsInTechnique(technique=technique)
+
+ # Comprobar siguiente posición sin calificar
+ (next_position, end_products) = CalificacionController.checkPositionWithoutRating(
+ positions=aligned_positions_in_order,
+ user_cata=request.user.user_catador,
+ repetition=technique.repeticion,
+ technique=technique,
+ num_words=len(words)
+ )
+
+ # Si no hay productos se finaliza la sesion
+ if end_products:
+ ParticipacionController.finishSession(self.participation)
+ params = {"code_sesion": self.session.codigo_sesion}
+ return redirect(reverse('cata_system:catador_init_session', kwargs=params))
+
+ # Si devuelve una lista, tomar el primer elemento
+ if isinstance(next_position, list):
+ next_position = next_position[0]
+
+ # Producto a calificar ahora
+ product = next_position.id_producto
+ ctx["product"] = product
+
+ # Revisar las palabras para calificar
+ try:
+ rating = Calificacion.objects.get(
+ num_repeticion=technique.repeticion,
+ id_producto=product,
+ id_tecnica=technique,
+ id_catador=self.tester
+ )
+ there_rating = True
+ except Calificacion.DoesNotExist:
+ there_rating = False
+
+ # Si no hay calificaciones previas, usar todas las palabras
+ if not there_rating:
+ ctx["words"] = words
+ else:
+ ratings_product = rating.dato_calificacion.all()
+ # Filtrar palabras que faltan
+ words_to_use = PalabrasController.getWordsWithoutData(
+ recoreded_data=ratings_product,
+ words=words
+ )
+ ctx["words"] = words_to_use
+
+ # Escala y etiquetas relacionadas
+ scale = EscalaController.getScaleByTechnique(technique=technique)
+ ctx["scale"] = scale
+ ctx["type_scale"] = scale.id_tipo_escala.nombre_escala
+ ctx["tags"] = EscalaController.getRelatedTagsInScale(scale=scale)
+
+ return render(request, self.current_directory, ctx)
+
+ def controllGetRATA(self, request: HttpRequest):
+ technique = self.session.tecnica
+ self.participation = Participacion.objects.get(
+ tecnica=technique, catador=request.user.user_catador)
+
+ self.context["session"] = self.session
+
+ products_in_technique = Producto.objects.filter(id_tecnica=technique)
+
+ words = PalabrasController.getWordsInTechnique(technique=technique)
+
+ use_product: Producto = None
+ use_words: list[Palabra] = None
+
+ # Revisamos el producto que le falten calificaciones
+ for current_product in products_in_technique:
+ try:
+ rating = Calificacion.objects.get(
+ num_repeticion=technique.repeticion,
+ id_producto=current_product,
+ id_tecnica=technique,
+ id_catador=self.tester
+ )
+ there_rating = True
+ except Calificacion.DoesNotExist:
+ there_rating = False
+
+ # Si no hay calificacion mandamos el producto actual y todas la palabras
+ if not there_rating:
+ use_product = current_product
+ use_words = words
+ break
+
+ # Obtener los datos asociados para la calificacion para ver que palabras quedan por calificar
+ recoreded_data = rating.dato_calificacion.all()
+
+ if not recoreded_data:
+ # Si no hay datos entonces devolver el producto con todas las palabras
+ use_product = current_product
+ use_words = words
+ break
+ else:
+ words_to_use = PalabrasController.getWordsWithoutData(
+ recoreded_data=recoreded_data, words=words)
+
+ # Si quedan palabras por calificar mandar las palabras con el producto
+ if not isinstance(words_to_use, dict) and words_to_use:
+ use_product = current_product
+ use_words = words_to_use
+ break
+
+ # Si no hay producto que falta por calificar finalizar sesion para el Catador
+ if not use_product:
+ updated_participation = ParticipacionController.finishSession(
+ self.participation)
+ params = {
+ "code_sesion": self.session.codigo_sesion
+ }
+ return redirect(reverse(self.previus_directory, kwargs=params))
+
+ self.context["product"] = use_product
+ self.context["words"] = use_words
+
+ # Agregar informacion de la escala
+ scale = EscalaController.getScaleByTechnique(technique=technique)
+ self.context["scale"] = scale
+ self.context["type_scale"] = scale.id_tipo_escala.nombre_escala
+
+ use_tags = EscalaController.getRelatedTagsInScale(scale=scale)
+ self.context["tags"] = use_tags
+
+ return render(request, self.current_directory, self.context)
diff --git a/tecnicas/controllers/views_controller/sessions_tester/init_session_tester_controller.py b/tecnicas/controllers/views_controller/sessions_tester/init_session_tester_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..29102a123cf00bdd6387553378bd3eef0f915724
--- /dev/null
+++ b/tecnicas/controllers/views_controller/sessions_tester/init_session_tester_controller.py
@@ -0,0 +1,164 @@
+from django.db import transaction
+from django.http import HttpRequest
+from django.shortcuts import render, redirect
+from django.urls import reverse
+from tecnicas.models import Catador, SesionSensorial, Orden, Participacion, Producto, EsAtributo, Calificacion, EsVocabulario, Dato
+from tecnicas.controllers import ParticipacionController
+from tecnicas.utils import controller_error, shuffleArray
+
+
+class InitSessionTesterController():
+ tester: Catador
+ session: SesionSensorial
+ order: Orden | dict
+ current_direction = "tecnicas/forms_tester/init_session.html"
+ escalas_direction = "cata_system:session_convencional"
+
+ def __init__(self, sensorial_session: SesionSensorial, user_tester: Catador):
+ self.tester = user_tester
+ self.session = sensorial_session
+
+ def controllGetEscalas(self, request: HttpRequest):
+ context = {
+ "session": self.session,
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
+ }
+
+ order = self.checkAndAssignOrder()
+ if isinstance(order, dict):
+ context["error"] = order["error"]
+ return render(request, self.current_direction, context)
+
+ is_end = self.isEndedSessionEscalas()
+
+ request.session["id_order"] = order.id
+ context["has_ended"] = is_end
+
+ if is_end:
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
+
+ if "error" in request.GET:
+ context["error"] = request.GET["error"]
+
+ return render(request, self.current_direction, context)
+
+ def controllPostEscalas(self, request: HttpRequest):
+ context = {
+ "session": self.session,
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
+ }
+
+ if request.POST["action"] == "start_posting":
+ parameters = {
+ "code_sesion": self.session.codigo_sesion
+ }
+
+ is_end = self.isEndedSessionEscalas()
+ if is_end:
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
+ return render(request, self.current_direction, context)
+
+ update_participation = ParticipacionController.enterSession(
+ tester=request.user.user_catador, session=self.session)
+ if isinstance(update_participation, dict):
+ context["error"] = update_participation["error"]
+ return render(request, self.current_direction, context)
+
+ request.session["id_participation"] = update_participation.id
+ return redirect(reverse(self.escalas_direction, kwargs=parameters))
+ elif request.POST["action"] == "exit_session":
+ response = ParticipacionController.outSession(
+ tester=request.user.user_catador, session=self.session)
+ if isinstance(response, dict):
+ context["error"] = response["error"]
+ return render(request, self.current_direction, context)
+ else:
+ context["error"] = "Acción sin especificar"
+ return render(request, self.current_direction, context)
+
+ def controllGetRATA(self, request: HttpRequest):
+ context = {
+ "session": self.session,
+ "type_technique": self.session.tecnica.tipo_tecnica.nombre_tecnica
+ }
+
+ is_end = self.isEndedSessionEscalas()
+
+ context["has_ended"] = is_end
+
+ if is_end:
+ context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador"
+
+ return render(request, self.current_direction, context)
+
+ def assignOrder(self):
+ with transaction.atomic():
+ orders_without_tester = list(Orden.objects.select_for_update().filter(
+ id_tecnica=self.session.tecnica, id_catador=None))
+
+ if not orders_without_tester:
+ return controller_error("Las ordenes se han acabado")
+
+ shuffle_orders = shuffleArray(orders_without_tester)
+ self.order_to_assign = shuffle_orders.pop()
+
+ self.order_to_assign.id_catador = self.tester
+ self.order_to_assign.save()
+
+ return self.order_to_assign
+
+ def checkAndAssignOrder(self):
+ try:
+ self.order_to_assign = Orden.objects.get(
+ id_tecnica=self.session.tecnica, id_catador=self.tester)
+ except Orden.DoesNotExist:
+ create = self.assignOrder()
+ if isinstance(create, dict):
+ return create
+ return self.order_to_assign
+
+ def isEndedSessionEscalas(self):
+ try:
+ participation = Participacion.objects.get(
+ catador=self.tester, tecnica=self.session.tecnica)
+ self.session.refresh_from_db()
+
+ # ////////////////////////////////////////////////////////////// #
+ #
+ # numero_datos_esperadas = num_productos * num_palabras
+ # Si numero_datos_esperadas es igual a numero_datos_actuales en la repetcion R
+ # Ha terminado la repeticion
+ #
+ # ////////////////////////////////////////////////////////////// #
+
+ if participation.finalizado:
+ num_products = Producto.objects.filter(
+ id_tecnica=self.session.tecnica).count()
+
+ technique = self.session.tecnica
+ style_words = technique.id_estilo.nombre_estilo
+
+ num_words: int
+
+ if style_words == "atributos":
+ num_words = EsAtributo.objects.get(
+ id_tecnica=self.session.tecnica).palabras.count()
+ elif style_words == "vocabulario":
+ num_words = EsVocabulario.objects.get(
+ id_tecnica=self.session.tecnica).id_vocabulario.palabras.count()
+
+ expected_ratings_repetition = num_products * num_words
+
+ num_ratings_now = Dato.objects.filter(
+ id_calificacion__id_catador=self.tester,
+ id_calificacion__id_tecnica=technique,
+ id_calificacion__num_repeticion=technique.repeticion
+ ).count()
+
+ is_end = num_ratings_now >= expected_ratings_repetition
+
+ return is_end
+ else:
+ return participation.finalizado
+ except Participacion.DoesNotExist:
+ return controller_error("No se ha encontrado la participación")
diff --git a/tecnicas/controllers/views_controller/list_sessions_tester_controller.py b/tecnicas/controllers/views_controller/sessions_tester/list_sessions_tester_controller.py
similarity index 100%
rename from tecnicas/controllers/views_controller/list_sessions_tester_controller.py
rename to tecnicas/controllers/views_controller/sessions_tester/list_sessions_tester_controller.py
diff --git a/tecnicas/controllers/views_controller/login_tester_controller.py b/tecnicas/controllers/views_controller/sessions_tester/login_session_tester_controller.py
similarity index 69%
rename from tecnicas/controllers/views_controller/login_tester_controller.py
rename to tecnicas/controllers/views_controller/sessions_tester/login_session_tester_controller.py
index 1b9c7849593f2f58ae9c3849537783b4b9608bc4..452bf5dd9c524e41223a3f78be590e209ae32f64 100644
--- a/tecnicas/controllers/views_controller/login_tester_controller.py
+++ b/tecnicas/controllers/views_controller/sessions_tester/login_session_tester_controller.py
@@ -6,7 +6,7 @@ from tecnicas.models import Catador, SesionSensorial, Participacion
from tecnicas.utils import controller_error
-class LoginTesterController():
+class LoginSessionTesterController():
tester: Catador
session: SesionSensorial
taster_participation: Participacion
@@ -33,38 +33,38 @@ class LoginTesterController():
context["error"] = "La sesión no está activa actualmente"
return render(request, self.current_direcction, context)
- if self.session.tecnica.repeticion > 1:
+ if self.session.tecnica.repeticion == 1:
try:
self.taster_participation = Participacion.objects.get(
tecnica=self.session.tecnica, catador=self.tester)
context["error"] = "Usted ya esta dentro de la sesión"
return render(request, self.current_direcction, context)
except Participacion.DoesNotExist:
- 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)
+ 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()
+ 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)
+ 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))
+ 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))
+ else:
+ context["error"] = "Ya no es posible ingresar a la sesión"
+ return render(request, self.current_direcction, context)
def validateEntryRATA(self, request: HttpRequest):
context = {}
diff --git a/tecnicas/controllers/views_controller/vocabulary_manage/create_vocabulary_controller.py b/tecnicas/controllers/views_controller/vocabulary_manage/create_vocabulary_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf86d76cac1508fcecb9a9933aee004a768f864a
--- /dev/null
+++ b/tecnicas/controllers/views_controller/vocabulary_manage/create_vocabulary_controller.py
@@ -0,0 +1,110 @@
+from django.shortcuts import render
+from django.db import IntegrityError
+from django.http import HttpRequest
+from tecnicas.forms import WordForm
+from tecnicas.models import Vocabulario, Palabra
+import json
+
+
+class CreateVocabularyController():
+ context = {}
+ current_url = "tecnicas/manage_vocabulary/create-vocabulary.html"
+
+ def __init__(self, form_word: WordForm = WordForm(), list_words: list = []):
+ self.context["form_word"] = form_word
+ self.context["words"] = list_words
+
+ def controllGet(self, request: HttpRequest):
+ self.context = {
+ "form_word": WordForm(),
+ "words": []
+ }
+
+ if "name_voca" in request.GET:
+ current = Vocabulario.objects.get(
+ nombre_vocabulario=request.GET["name_voca"])
+ self.context["name_vacabulary"] = current.nombre_vocabulario
+ self.context["words"] = current.palabras.all()
+
+ return render(request, self.current_url, self.context)
+
+ def controllPost(self, request: HttpRequest):
+ self.context = {
+ "form_word": self.context["form_word"],
+ "words": self.context["words"],
+ }
+
+ if "nombre_vocabulario" not in request.POST:
+ self.context["error"] = "Nombre de vocabulario requerido"
+ return render(request, self.current_url, self.context)
+
+ new_vocabulary_name = request.POST.get("nombre_vocabulario").strip()
+ is_update = request.POST.get("is_update")
+
+ print(is_update)
+
+ if is_update:
+ if "original_name" not in request.POST:
+ self.context["error"] = "Nombre original de vocabulario requerido"
+ return render(request, self.current_url, self.context)
+
+ original_name = request.POST["original_name"].strip()
+
+ if original_name != new_vocabulary_name:
+ if Vocabulario.objects.filter(nombre_vocabulario=new_vocabulary_name).exists():
+ self.context["error"] = "Ya existe un vocabulario con el nombre pasado"
+ return render(request, self.current_url, self.context)
+
+ try:
+ current_vocababulary = Vocabulario.objects.get(
+ nombre_vocabulario=original_name)
+ except Vocabulario.DoesNotExist:
+ self.context["error"] = "No existe un vocabulario con ese nombre"
+ return render(request, self.current_url, self.context)
+
+ words_json = request.POST.get("words", "")
+ if words_json:
+ try:
+ words_list = json.loads(words_json)
+
+ ids = [int(w.get("id", 0))
+ for w in words_list if str(w.get("id", "")).isdigit()]
+
+ words = Palabra.objects.filter(id__in=ids)
+
+ current_vocababulary.palabras.set(words)
+ current_vocababulary.nombre_vocabulario = new_vocabulary_name
+ current_vocababulary.save()
+ except (json.JSONDecodeError, ValueError):
+ self.context["error"] = 'Ocurrió un error al revisar las palabras, revise “Ver vocabularios”, para reasignar las palabras'
+ return render(request, self.current_url, self.context)
+
+ self.context["message"] = 'Vocabulario creado con éxito, puedes revisarlo en "Ver vocabularios"'
+ return render(request, self.current_url, self.context)
+ elif not is_update:
+ try:
+ new_vocababulary = Vocabulario.objects.create(
+ nombre_vocabulario=new_vocabulary_name)
+ except IntegrityError:
+ self.context["error"] = "Ya existe un vocabulario con ese nombre"
+ return render(request, self.current_url, self.context)
+
+ words_json = request.POST.get("words", "")
+ if words_json:
+ try:
+ words_list = json.loads(words_json)
+
+ ids = [int(w.get("id", 0))
+ for w in words_list if str(w.get("id", "")).isdigit()]
+
+ words = Palabra.objects.filter(id__in=ids)
+
+ new_vocababulary.palabras.add(*words)
+ except (json.JSONDecodeError, ValueError):
+ self.context["error"] = 'Ocurrió un error al revisar las palabras, revise “Ver vocabularios”, para reasignar las palabras'
+ return render(request, self.current_url, self.context)
+
+ self.context["message"] = 'Vocabulario actualziado con éxito, puedes revisarlo en "Ver vocabularios"'
+ return render(request, self.current_url, self.context)
+ else:
+ pass
diff --git a/tecnicas/controllers/views_controller/vocabulary_manage/list_vocabulary_controller.py b/tecnicas/controllers/views_controller/vocabulary_manage/list_vocabulary_controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..6b8daeaa11b4fa7abfa5259a2182a0c015f189c4
--- /dev/null
+++ b/tecnicas/controllers/views_controller/vocabulary_manage/list_vocabulary_controller.py
@@ -0,0 +1,48 @@
+from django.shortcuts import render
+from django.http import HttpRequest
+from django.core.paginator import Paginator, PageNotAnInteger
+from tecnicas.models import Vocabulario
+from tecnicas.utils import controller_error
+
+
+class ListVocabularyController():
+ current_url = "tecnicas/manage_vocabulary/list-vocabulary.html"
+
+ def __init__(self):
+ pass
+
+ def controllGet(self, request: HttpRequest, page: int):
+ context = {}
+
+ info_element_page = self.getVocabularys(page)
+
+ if isinstance(info_element_page, dict):
+ context["error"] = info_element_page["error"]
+ return render(request, self.current_url, context)
+
+ (vocabularies_in_page, is_last_page, current_page) = info_element_page
+
+ context["vocabularies"] = vocabularies_in_page
+ context["last_page"] = is_last_page
+ context["num_page"] = current_page
+
+ return render(request, self.current_url, context)
+
+ def getVocabularys(self, num_page: int):
+ elements_by_page = 6
+
+ queryset = Vocabulario.objects.all().order_by('-creado')
+
+ paginator = Paginator(queryset, elements_by_page)
+ try:
+ vocabularies_in_page = paginator.page(num_page)
+ except PageNotAnInteger:
+ return controller_error("índice inválido")
+
+ if not vocabularies_in_page.object_list:
+ return controller_error("Sin registros de Participaciones")
+
+ current_page = vocabularies_in_page.number
+ is_last_page = not current_page < paginator.num_pages
+
+ return (vocabularies_in_page, is_last_page, current_page)
diff --git a/tecnicas/forms/__init__.py b/tecnicas/forms/__init__.py
index 329841ba3eb6ba1f3620b692472674db8633a11c..75282f742f8f495728d7adf450e5403b5fb073a3 100644
--- a/tecnicas/forms/__init__.py
+++ b/tecnicas/forms/__init__.py
@@ -1,6 +1,9 @@
-from .sesion_basic_form import SesionBasicForm
-from .sesion_tags_form import SesionTagsForm
+from .create_session.sesion_basic_form import SesionBasicForm
+from .create_session.sesiob_basic_cata_form import SesionBasicCATAForm
+from .create_session.sesion_tags_form import SesionTagsForm
+
from .etiqueta_form import EtiquetaForm
from .codes_form import CodesForm
from .catador_form import CatadorForm
-from .word_form import WordForm
\ No newline at end of file
+from .word_form import WordForm
+from .vocabulary_select import VocabularioSelectForm
diff --git a/tecnicas/forms/create_session/sesiob_basic_cata_form.py b/tecnicas/forms/create_session/sesiob_basic_cata_form.py
new file mode 100644
index 0000000000000000000000000000000000000000..b578857573b1257f2dc2dc27b36d488def2f559a
--- /dev/null
+++ b/tecnicas/forms/create_session/sesiob_basic_cata_form.py
@@ -0,0 +1,27 @@
+from django import forms
+from tecnicas.models import EstiloPalabra
+
+
+class SesionBasicCATAForm(forms.Form):
+ nombre_sesion = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
+ "class": "bg-surface-ligt border-b-1 text-center w-full p-1",
+ "name": "nombre_sesion",
+ "placeholder": "Ej. Mermelada de mango picante"
+ }), required=False)
+
+ numero_productos = forms.IntegerField(widget=forms.NumberInput(attrs={
+ "class": "bg-surface-ligt p-1 border-b-1 text-center w-full",
+ "placeholder": "Solo números"
+ }), required=True)
+
+ 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, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ 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",
+ }), required=True, initial=EstiloPalabra.objects.first())
diff --git a/tecnicas/forms/sesion_basic_form.py b/tecnicas/forms/create_session/sesion_basic_form.py
similarity index 97%
rename from tecnicas/forms/sesion_basic_form.py
rename to tecnicas/forms/create_session/sesion_basic_form.py
index 87529c24fbe3cdcfbdcc4f4353d631d81ce9a189..34fef6e39575562583ac21e8e470be0eeb7874af 100644
--- a/tecnicas/forms/sesion_basic_form.py
+++ b/tecnicas/forms/create_session/sesion_basic_form.py
@@ -1,7 +1,5 @@
from django import forms
-from ..models import TipoEscala
-from ..models import TipoTecnica
-from ..models import EstiloPalabra
+from tecnicas.models import TipoEscala, EstiloPalabra
class SesionBasicForm(forms.Form):
diff --git a/tecnicas/forms/sesion_tags_form.py b/tecnicas/forms/create_session/sesion_tags_form.py
similarity index 98%
rename from tecnicas/forms/sesion_tags_form.py
rename to tecnicas/forms/create_session/sesion_tags_form.py
index 8b2c85603f4bed97438a7ff16a57132cfdfddbda..07e5980317aa906e0ba0ab1616f635dea70d21f6 100644
--- a/tecnicas/forms/sesion_tags_form.py
+++ b/tecnicas/forms/create_session/sesion_tags_form.py
@@ -1,5 +1,5 @@
from django import forms
-from ..models import Etiqueta
+from tecnicas.models import Etiqueta
class SesionTagsForm(forms.Form):
diff --git a/tecnicas/forms/vocabulary_select.py b/tecnicas/forms/vocabulary_select.py
new file mode 100644
index 0000000000000000000000000000000000000000..6b52c97fb536c0937a1fb616b1e3f49ef0da7c8f
--- /dev/null
+++ b/tecnicas/forms/vocabulary_select.py
@@ -0,0 +1,15 @@
+from django import forms
+from tecnicas.models import Vocabulario
+
+
+class VocabularioSelectForm(forms.Form):
+ vocabulario = forms.ModelChoiceField(
+ queryset=Vocabulario.objects.all(),
+ required=True,
+ label="Selecciona un vocabulario",
+ empty_label="-- Selecciona uno --",
+ widget=forms.Select(attrs={
+ "class": "w-full border rounded p-4 bg-surface-sweet",
+ "id": "vocabulario"
+ })
+ )
diff --git a/tecnicas/migrations/0021_rename_nomre_vocabulario_vocabulario_nombre_vocabulario_and_more.py b/tecnicas/migrations/0021_rename_nomre_vocabulario_vocabulario_nombre_vocabulario_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..88b4dd061e4b9d51bcde12083c6ff94ac64c5087
--- /dev/null
+++ b/tecnicas/migrations/0021_rename_nomre_vocabulario_vocabulario_nombre_vocabulario_and_more.py
@@ -0,0 +1,30 @@
+# Generated by Django 5.2.1 on 2025-11-06 21:46
+
+import django.utils.timezone
+import shortuuid.main
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tecnicas', '0020_remove_catador_apellido_remove_catador_correo_and_more'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='vocabulario',
+ old_name='nomre_vocabulario',
+ new_name='nombre_vocabulario',
+ ),
+ migrations.AddField(
+ model_name='vocabulario',
+ name='creado',
+ field=models.DateTimeField(default=django.utils.timezone.now),
+ ),
+ migrations.AlterField(
+ model_name='sesionsensorial',
+ name='codigo_sesion',
+ field=models.CharField(default=shortuuid.main.ShortUUID.uuid, editable=False, max_length=22, primary_key=True, serialize=False),
+ ),
+ ]
diff --git a/tecnicas/models/calificacion.py b/tecnicas/models/calificacion.py
index 795eef44ba151096cc6116372d29100639578d68..b2ac5d4189b7ee41aeb5739093b6c59e20ed1ae8 100644
--- a/tecnicas/models/calificacion.py
+++ b/tecnicas/models/calificacion.py
@@ -8,4 +8,7 @@ class Calificacion(models.Model):
num_repeticion = models.IntegerField()
id_producto = models.ForeignKey(Producto, on_delete=models.CASCADE, related_name="calificacion_producto")
id_tecnica = models.ForeignKey(Tecnica, on_delete=models.CASCADE, related_name="calificacion_tecnica")
- id_catador = models.ForeignKey(Catador, on_delete=models.CASCADE, related_name="calificacion_catador")
\ No newline at end of file
+ id_catador = models.ForeignKey(Catador, on_delete=models.CASCADE, related_name="calificacion_catador")
+
+ def __str__(self):
+ return f"{self.id} - {self.id_tecnica.sesion_tecnica} - {self.num_repeticion} - {self.id_catador.user.username}"
\ No newline at end of file
diff --git a/tecnicas/models/dato.py b/tecnicas/models/dato.py
index 7ed8babbb249be26a55d51883b68f5643db24d6d..7b6537376f233eaffa98529c33a9e11e6fbf66fa 100644
--- a/tecnicas/models/dato.py
+++ b/tecnicas/models/dato.py
@@ -11,4 +11,4 @@ class Dato(models.Model):
Calificacion, on_delete=models.CASCADE, related_name="dato_calificacion")
def __str__(self):
- return f"{self.id_palabra.nombre_palabra} - {self.id_calificacion.id_producto.codigoProducto} - {self.id_calificacion.id_catador.usuarioCatador}"
+ return f"{self.id_calificacion.id_tecnica.sesion_tecnica}- {self.id_calificacion.num_repeticion} - {self.id_palabra.nombre_palabra} - {self.id_calificacion.id_producto.codigoProducto} - {self.id_calificacion.id_catador.user.username}"
diff --git a/tecnicas/models/dato_valor.py b/tecnicas/models/dato_valor.py
index 277ff25827f9cf4b77fc9b5318b591516ae641ce..ea7b9a3618c20fa70b068c28638d82cb54524f75 100644
--- a/tecnicas/models/dato_valor.py
+++ b/tecnicas/models/dato_valor.py
@@ -9,7 +9,7 @@ class ValorDecimal(models.Model):
valor = models.FloatField()
def __str__(self):
- return f"{self.id} - {self.id_dato.id_palabra}: {self.valor} - {self.id_dato.id_calificacion.id_catador.usuarioCatador}"
+ return f"{self.id} - {self.id_dato.id_palabra}: {self.valor} - {self.id_dato.id_calificacion.id_catador.user.username}"
class ValorBooleano(models.Model):
@@ -18,4 +18,4 @@ class ValorBooleano(models.Model):
valor = models.BooleanField()
def __str__(self):
- return f"{self.id} - {self.id_dato.id_palabra}: {self.valor} - {self.id_dato.id_calificacion.id_catador.usuarioCatador}"
+ return f"{self.id} - {self.id_dato.id_palabra}: {self.valor} - {self.id_dato.id_calificacion.id_catador.user.username}"
diff --git a/tecnicas/models/vocabulario.py b/tecnicas/models/vocabulario.py
index eb6f0b3fd609d69b71653b36fa46988ad552cc65..6d30efb2eb844b03b086354301d73b015160c4a9 100644
--- a/tecnicas/models/vocabulario.py
+++ b/tecnicas/models/vocabulario.py
@@ -1,10 +1,13 @@
from django.db import models
-
+from django.utils import timezone
from .palabra import Palabra
+
class Vocabulario(models.Model):
- nomre_vocabulario = models.CharField(max_length=255, unique=True)
- palabras = models.ManyToManyField(Palabra, related_name="vovabulario_palabras")
+ nombre_vocabulario = models.CharField(max_length=255, unique=True)
+ palabras = models.ManyToManyField(
+ Palabra, related_name="vovabulario_palabras")
+ creado = models.DateTimeField(default=timezone.now)
def __str__(self):
- return self.nomre_vocabulario
\ No newline at end of file
+ return self.nombre_vocabulario
diff --git a/tecnicas/static/img/letters.webp b/tecnicas/static/img/letters.webp
new file mode 100644
index 0000000000000000000000000000000000000000..f977a7553deff3506147fc000c21bda6f75e33c2
Binary files /dev/null and b/tecnicas/static/img/letters.webp differ
diff --git a/tecnicas/static/js/choose-vocabulary.js b/tecnicas/static/js/choose-vocabulary.js
new file mode 100644
index 0000000000000000000000000000000000000000..9496cc5f02662070543906920dd20719af359089
--- /dev/null
+++ b/tecnicas/static/js/choose-vocabulary.js
@@ -0,0 +1,67 @@
+document.addEventListener("DOMContentLoaded", () => {
+ const vocabSelect = document.getElementById("vocabulario");
+ const wordsList = document.getElementById("palabras-lista");
+ const formNextStep = document.getElementById("cts-create-session");
+ let vocabularyId = "";
+
+ vocabSelect.addEventListener("change", async (e) => {
+ const vocabId = e.target.value;
+ vocabularyId = vocabId;
+ console.log(e.target);
+ console.log(vocabId);
+
+ wordsList.innerHTML =
+ "
Cargando... ";
+
+ if (!vocabId) {
+ wordsList.innerHTML =
+ "Selecciona un vocabulario para ver sus palabras ";
+ return;
+ }
+
+ url_fetch = `api/vocabulario/${vocabId}/palabras`;
+
+ try {
+ const response = await fetch(url_fetch, { method: "GET" });
+ if (!response.ok) throw new Error("Error en la petición");
+ const json_response = await response.json();
+
+ words = json_response.data.words;
+
+ if (words === 0) {
+ wordsList.innerHTML =
+ "No hay palabras asociadas ";
+ return;
+ }
+
+ wordsList.innerHTML = "";
+ words.forEach((p) => {
+ const li = document.createElement("li");
+ li.textContent = p.nombre_palabra;
+ li.className =
+ "bg-surface-sweet text-black rounded font-bold text-lg px-4 py-3";
+ wordsList.appendChild(li);
+ });
+ } catch (err) {
+ wordsList.innerHTML =
+ "Error al cargar las palabras ";
+ console.error(err);
+ }
+ });
+
+ formNextStep.addEventListener("submit", (e) => {
+ if (!vocabularyId) {
+ e.preventDefault();
+ wordsList.innerHTML =
+ "Seleccione un Vocabulario ";
+ return;
+ }
+
+ const useVocabulary = document.createElement("input");
+ useVocabulary.type = "hidden";
+ useVocabulary.name = "vocabulario";
+ useVocabulary.value = vocabularyId;
+
+ formNextStep.appendChild(useVocabulary);
+ });
+});
diff --git a/tecnicas/static/js/created-scale.js b/tecnicas/static/js/created-scale.js
index 92980f87dafce02da38102709e80f47a80b70b24..7854f9d6cb5e0a45aab08d577edb4dc82fca7d6a 100644
--- a/tecnicas/static/js/created-scale.js
+++ b/tecnicas/static/js/created-scale.js
@@ -54,11 +54,13 @@ async function sendRating(word) {
.querySelector(".id-product").textContent;
const idWord = formRatingWord.querySelector(".id-word").textContent;
+ const idTechnique = document.querySelector(".ct-input-id-tech").value
dataForm.set("code-product", codeProduct);
dataForm.set("id-product", idProduct);
dataForm.set("name-word", word);
dataForm.set("id-word", idWord);
+ dataForm.set("id-technique", idTechnique);
try {
const respone = await fetch(url, {
diff --git a/tecnicas/static/js/created-vocabulary.js b/tecnicas/static/js/created-vocabulary.js
new file mode 100644
index 0000000000000000000000000000000000000000..c5c25be883c35eb4dcfb7adb6d00cb1a3f585215
--- /dev/null
+++ b/tecnicas/static/js/created-vocabulary.js
@@ -0,0 +1,48 @@
+// **************************************
+// Create Vocabulary
+// **************************************
+async function submitSelectWords(classNanmeForm, update = false) {
+ const form = document.querySelector(`.${classNanmeForm}`);
+
+ const name_vocabulary = form.querySelector(".cts-name-voca").value;
+ if (!name_vocabulary || name_vocabulary == "") {
+ spanNotificationRed("Se requiere el nombre del vocabulario");
+ return;
+ }
+
+ if (listWordsSelect.length === 0) {
+ spanNotificationRed("Debe seleccionar al menos una palabra");
+ return;
+ }
+
+ const wordsInput = document.createElement("input");
+ wordsInput.type = "hidden";
+ wordsInput.name = "words";
+ wordsInput.value = JSON.stringify(listWordsSelect);
+
+ const [isUpdata, orinalName] = inputIsUpdateVocabulary(update);
+
+ form.appendChild(wordsInput);
+ form.appendChild(isUpdata);
+ if (orinalName) form.appendChild(orinalName);
+
+ form.submit();
+}
+
+function inputIsUpdateVocabulary(is_update = false) {
+ const isUpdata = document.createElement("input");
+ isUpdata.type = "hidden";
+ isUpdata.name = "is_update";
+ isUpdata.value = is_update;
+
+ if (is_update) {
+ const orinalName = document.querySelector(".cts-original-name").textContent;
+ const inputName = document.createElement("input");
+ inputName.type = "hidden";
+ inputName.name = "original_name";
+ inputName.value = orinalName;
+ return [isUpdata, inputName];
+ }
+
+ return [isUpdata, is_update];
+}
diff --git a/tecnicas/static/js/download-table-csv.js b/tecnicas/static/js/download-table-csv.js
new file mode 100644
index 0000000000000000000000000000000000000000..1e525453c2a5aa0bf5fbe1a55babcf923d65a13a
--- /dev/null
+++ b/tecnicas/static/js/download-table-csv.js
@@ -0,0 +1,78 @@
+document.addEventListener("DOMContentLoaded", function () {
+ const btn = document.getElementById("download-csv-btn");
+ if (!btn) return;
+
+ btn.addEventListener("click", function () {
+ // Try set the table in the page
+ let table = document.getElementById("convencional-table");
+ if (!table) {
+ const section = btn.closest("section");
+ if (section) table = section.querySelector("table");
+ }
+ if (!table) {
+ console.warn("No se encontró la tabla para descargar.");
+ return;
+ }
+
+ // helper to trim and normalize cell text
+ const cellText = (cell) => {
+ if (!cell) return "";
+ return String(cell.textContent || "").trim();
+ };
+
+ // Collect headers
+ const headers = [];
+ const ths = table.querySelectorAll("thead th");
+ ths.forEach((th) => headers.push(cellText(th)));
+
+ // Collect rows
+ const rows = [];
+ const trs = table.querySelectorAll("tbody tr");
+ trs.forEach((tr) => {
+ const cols = [];
+ const tds = tr.querySelectorAll("td");
+ tds.forEach((td) => cols.push(cellText(td)));
+ rows.push(cols);
+ });
+
+ // Convert to CSV string (escape quotes, wrap in quotes if needed)
+ const escapeValue = (val) => {
+ if (val == null) return "";
+ normalVal = val.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
+ const needsQuotes = /[",\n,]/.test(normalVal);
+ let v = String(normalVal).replace(/"/g, '""');
+ if (needsQuotes) v = `"${v}"`;
+ return v;
+ };
+
+ const lines = [];
+ if (headers.length) lines.push(headers.map(escapeValue).join(","));
+ rows.forEach((r) => lines.push(r.map(escapeValue).join(",")));
+
+ const csvContent = lines.join("\n");
+
+ // File name: data_{nombre_sesion or codigo_sesion}
+ const rawName = (btn.dataset.sessionName || "").trim();
+ const code = (btn.dataset.sessionCode || "").trim() || "session";
+ const namePart = rawName
+ ? rawName.replace(/[^a-zA-Z0-9-_áéíóúÁÉÍÓÚ ]/g, "").replace(/\s+/g, "_")
+ : code;
+ const fileName = `data_${namePart}.csv`;
+
+ // Create blob and force download
+ const blob = new Blob([csvContent], { type: "text/csv;charset=UTF-8;" });
+ if (navigator.msSaveBlob) {
+ navigator.msSaveBlob(blob, fileName);
+ } else {
+ const link = document.createElement("a");
+ const url = URL.createObjectURL(blob);
+ link.setAttribute("href", url);
+ link.setAttribute("download", fileName);
+ link.style.visibility = "hidden";
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ }
+ });
+});
diff --git a/tecnicas/static/js/panel-basic-cata.js b/tecnicas/static/js/panel-basic-cata.js
new file mode 100644
index 0000000000000000000000000000000000000000..ede4704608b11de3dbbe83f9638f2b2b2bee7f89
--- /dev/null
+++ b/tecnicas/static/js/panel-basic-cata.js
@@ -0,0 +1,31 @@
+const descriptons = {
+ atributos:
+ "Con el estilo atributos elijes las palabras para evaluar los productos",
+ vocabulario:
+ "Los vocabularios son un conjunto de palabras específicas para evaluar aspectos de un producto",
+};
+
+const helpStyleWords = document.querySelector(".cts-help-style-words");
+let radiosStyleWords;
+
+function initRadiosStyleWords() {
+ radiosStyleWords = document.getElementsByName("estilo_palabras");
+
+ for (let index = 0; index < radiosStyleWords.length; index++) {
+ const radio = radiosStyleWords[index];
+
+ if (radio.checked) {
+ const radioOption = radio.parentElement.textContent.trim();
+ const textHelp = descriptons[radioOption];
+ helpStyleWords.textContent = textHelp;
+ }
+
+ radio.parentElement.addEventListener("click", (e) => {
+ const radioOption = radio.parentElement.textContent.trim();
+ const textHelp = descriptons[radioOption];
+ helpStyleWords.textContent = textHelp;
+ });
+ }
+}
+
+initRadiosStyleWords();
diff --git a/tecnicas/static/js/panel-words.js b/tecnicas/static/js/panel-words.js
index 2a68fefff6f0e6076562a5282eff694ab4810239..d86d9aa0c15dd1df49e6c0a66ebbf9d2f70eac6b 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 = `/cata/api/palabras?${params}`;
+ const url = `api/palabras?${params}`;
try {
const respone = await fetch(url, {
diff --git a/tecnicas/templates/tecnicas/components/form-scale-continue.html b/tecnicas/templates/tecnicas/components/form-scale-continue.html
new file mode 100644
index 0000000000000000000000000000000000000000..81d4ceb823db8a5a565bb848a770d9f01317b36c
--- /dev/null
+++ b/tecnicas/templates/tecnicas/components/form-scale-continue.html
@@ -0,0 +1,62 @@
+
\ No newline at end of file
diff --git a/tecnicas/templates/tecnicas/components/form-scale-structure.html b/tecnicas/templates/tecnicas/components/form-scale-structure.html
new file mode 100644
index 0000000000000000000000000000000000000000..f1daa1858c46a8a68cf2ff1fa9faa2c92fb90810
--- /dev/null
+++ b/tecnicas/templates/tecnicas/components/form-scale-structure.html
@@ -0,0 +1,52 @@
+
\ No newline at end of file
diff --git a/tecnicas/templates/tecnicas/components/item_session_tester.html b/tecnicas/templates/tecnicas/components/item_session_tester.html
index 8224f00a46376b14ebb23b0149520c76aab0d1e6..1c2c9b94c468c01b33985b706d757d12314cef71 100644
--- a/tecnicas/templates/tecnicas/components/item_session_tester.html
+++ b/tecnicas/templates/tecnicas/components/item_session_tester.html
@@ -10,7 +10,7 @@
Código: {{ session.codigo_sesion }}
- Técnica: {{ session.tecnica.tipo_tecnica }}
+ Técnica: {{ session.tecnica.tipo_tecnica }}
Fecha: {{ session.fechaCreacion }}
Finazliado:
diff --git a/tecnicas/templates/tecnicas/components/item_vocabulary.html b/tecnicas/templates/tecnicas/components/item_vocabulary.html
new file mode 100644
index 0000000000000000000000000000000000000000..1d2a12ce73e3203a6fc33a767ebfe3fa68c78b73
--- /dev/null
+++ b/tecnicas/templates/tecnicas/components/item_vocabulary.html
@@ -0,0 +1,20 @@
+
+
+
+
+ {{ vocabulary.nombre_vocabulario }}
+
+
+
Creado en:
+
{{ vocabulary.creado }}
+
+
+
+
+
\ No newline at end of file
diff --git a/tecnicas/templates/tecnicas/components/table-convencional.html b/tecnicas/templates/tecnicas/components/table-convencional.html
index 5462ea533f87edb5758525b155c0c0af31e29d56..0f11db4798200c6e318362f83ad5c77c6332333d 100644
--- a/tecnicas/templates/tecnicas/components/table-convencional.html
+++ b/tecnicas/templates/tecnicas/components/table-convencional.html
@@ -1,10 +1,10 @@
+{% load static %}
- Repetición {{ repeticion }}
-
-
+
+ Repetición
Usuario
Producto
{% for palabra in palabras %}
@@ -13,34 +13,40 @@
- {% for usuario, productos in catadores.items %}
- {% for codigo, valores in productos.items %}
-
- {{ usuario }}
- {{ codigo }}
- {% for palabra in palabras %}
-
- {% with match=None %}
- {% for valor in valores %}
- {% if valor.nombre_palabra == palabra %}
- {{ valor.dato_valor }}
- {% with match=True %}{% endwith %}
- {% endif %}
+ {% for repeticion, catadores in calificaciones.items %}
+ {% for usuario, productos in catadores.items %}
+ {% for codigo, valores in productos.items %}
+
+ {{ repeticion }}
+ {{ usuario }}
+ {{ codigo }}
+ {% for palabra in palabras %}
+
+ {% with match=False %}
+ {% for valor in valores %}
+ {% if valor.nombre_palabra == palabra %}
+ {{ valor.dato_valor }}
+ {% with match=True %}{% endwith %}
+ {% endif %}
+ {% endfor %}
+ {% if match %}0{% endif %}
+ {% endwith %}
+
+ {% endfor %}
+
{% endfor %}
- {% if not match %}0{% endif %}
- {% endwith %}
-
{% endfor %}
-
- {% endfor %}
{% endfor %}
-
+
Descargar CSV
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/tecnicas/templates/tecnicas/create_sesion/conf-panel-vocabulary.html b/tecnicas/templates/tecnicas/create_sesion/conf-panel-vocabulary.html
new file mode 100644
index 0000000000000000000000000000000000000000..f24cb7ae290692c5cae45bc2fd6a2fa5dffa75b2
--- /dev/null
+++ b/tecnicas/templates/tecnicas/create_sesion/conf-panel-vocabulary.html
@@ -0,0 +1,62 @@
+{% extends 'tecnicas/layouts/base.html' %}
+{% load static %}
+
+{% block content %}
+
+
+
+ Elegir vocabulario
+
+
+
+
+ {% if error %}
+ {% include "../components/error-message.html" with message=error %}
+ {% endif %}
+ {% if message %}
+ {% include "../components/error-message.html" with message=message %}
+ {% endif %}
+
+
+
Palabras asociadas:
+
+ Selecciona un vocabulario para ver sus palabras
+
+
+
+
+
+
+{% endblock %}
+
+{% block extra_js %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html b/tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html
index 96b7e7077d887c22b5d0cc812f50300a1ea62fa5..3853c7ac8f6a0507ad8907bfc50c2c392f7a9c6b 100644
--- a/tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html
+++ b/tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html
@@ -7,13 +7,18 @@
Panel de configuración
+
{% if error %}
- {{ error }}
+ {% include "./components/error-message.html" with message=error %}
+ {% endif %}
+ {% if message %}
+ {% include "./components/error-message.html" with message=message %}
{% endif %}
+