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 @@ +
    + {% csrf_token %} +
    +
    + + + + +
    +
    + + +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    + + {{ tags.0.id_etiqueta }} + +
    +
    + + {{ tags.1.id_etiqueta }} + +
    +
    + + {{ tags.2.id_etiqueta }} + +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    \ 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 @@ +
    + {% csrf_token %} +
    +
    + + + +
    +
    +
    + +
    + {% for tag in tags %} +
    +
    + {% endfor %} +
    +
    + +
    + {% for tag in tags %} +
    + + {{ tag.id_etiqueta }} + +
    + {% endfor %} +
    +
    +
    +
    +
    + +
    +
    + + + +
    +
    +
    \ 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 }}

    -
    - +
    + {% for palabra in palabras %} @@ -13,34 +13,40 @@ - {% for usuario, productos in catadores.items %} - {% for codigo, valores in productos.items %} - - - - {% for palabra in palabras %} - + + + + {% for palabra in palabras %} + + {% endfor %} + {% endfor %} - {% if not match %}0{% endif %} - {% endwith %} - {% endfor %} - - {% endfor %} {% endfor %}
    Repetición Usuario Producto
    {{ usuario }}{{ codigo }} - {% 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 }} + {% 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 %} +
    -
    -
    \ 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
    • +
    +
    + +
    +
    + {% csrf_token %} + +
    +
    +
    +
    +{% 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 %} +
    {% csrf_token %}

    Codificar productos

    -
    +

    Puede dejar los campos como están para usar los códigos predefinidos. @@ -47,7 +52,6 @@

    {% endif %} -
    + + + +
    +
    +
    +
    +{% endblock %} + +{% block extra_js %} + +{% endblock %} \ No newline at end of file diff --git a/tecnicas/templates/tecnicas/forms_tester/convencional.html b/tecnicas/templates/tecnicas/forms_tester/convencional.html index 4d7028e73b507ef89a415697ea8fb904e6af6075..1184b2f7fc7f18c0da904fa2c2f05132d9f51f6a 100644 --- a/tecnicas/templates/tecnicas/forms_tester/convencional.html +++ b/tecnicas/templates/tecnicas/forms_tester/convencional.html @@ -37,7 +37,8 @@

    - Sesión usando
    técnica Convencional + Sesión usando
    técnica + {{ session.tecnica.tipo_tecnica }}

    + + {% if error %}
    @@ -95,132 +101,17 @@
    - {% if type_scale == "continua" %} - {% for word in words %} -
    - {% csrf_token %} -
    -
    - - - - -
    -
    - - -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    - - {{ tags.0.id_etiqueta }} - -
    -
    - - {{ tags.1.id_etiqueta }} - -
    -
    - - {{ tags.2.id_etiqueta }} - -
    -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    - {% endfor %} - {% elif type_scale == "estructurada" %} - {% for word in words %} -
    - {% csrf_token %} -
    -
    - - - -
    -
    -
    - -
    - {% for tag in tags %} -
    -
    - {% endfor %} -
    -
    - -
    - {% for tag in tags %} -
    - - {{ tag.id_etiqueta }} - -
    - {% endfor %} -
    -
    -
    -
    -
    - -
    -
    - - - -
    -
    -
    - {% endfor %} - {% endif %} + {% with path_con="../components/form-scale-continue.html" path_str="../components/form-scale-structure.html" %} + {% if type_scale == "continua" %} + {% for word in words %} + {% include path_con with word=word tags=tags scale=scale id_tecnica=session.tecnica.id %} + {% endfor %} + {% elif type_scale == "estructurada" %} + {% for word in words %} + {% include path_str with word=word tags=tags scale=scale %} + {% endfor %} + {% endif %} + {% endwith %}
    diff --git a/tecnicas/templates/tecnicas/forms_tester/init_session.html b/tecnicas/templates/tecnicas/forms_tester/init_session.html index 57b84d86f9a603bfc3b587f0f634d00abfb6bb45..2c84810264133627d06bc78bcf56b53113e6fa7f 100644 --- a/tecnicas/templates/tecnicas/forms_tester/init_session.html +++ b/tecnicas/templates/tecnicas/forms_tester/init_session.html @@ -5,7 +5,7 @@ {% block content %}
    -
    +

    Panel principal de Catadores @@ -19,30 +19,15 @@

    {% if error %} -
    - -
    -

    - {{ error }} -

    -
    + {% include "../components/error-message.html" with message=error %} {% endif %} - {% if message %} -
    - -
    -

    - {{ message }} -

    -
    + {% include "../components/error-message.html" with message=message %} {% endif %} -
    - {% if session %}

    Código: @@ -68,16 +53,22 @@

    + {% if type_technique == "escalas" or type_technique == "rata" or type_technique == "cata" %}

    Esta sesión usa el estilo de palabras “{{ session.tecnica.id_estilo }}

    + {% endif %}

    + {% if type_technique == "escalas" %} Repetición número {{ session.tecnica.repeticion }} + {% else %} + Única repetición + {% endif %}

    @@ -86,13 +77,6 @@ {{ session.tecnica.instrucciones }}

    - {% else %} -
    -

    - Ha ocurrido un error en renderizar esta vista -

    -
    - {% endif %}

    diff --git a/tecnicas/templates/tecnicas/main-panel.html b/tecnicas/templates/tecnicas/main-panel.html index 22f85c4d086e3c182876a038695672cdb65db5a5..36e7390e5caa3586993cd674379e7c67f355e727 100644 --- a/tecnicas/templates/tecnicas/main-panel.html +++ b/tecnicas/templates/tecnicas/main-panel.html @@ -6,13 +6,13 @@ {% block content %}
    -
    +

    Panel Principal

    -
    + class="flex max-sm:flex-col flex-wrap justify-center max-sm:gap-4 items-center p-8 w-full bg-surface-card rounded-md shadow-lg"> +
    Nombre Presentador

    {{ name }}

    @@ -23,9 +23,9 @@

    {{ cumple }}

    -
    +
    @@ -37,32 +37,29 @@ {% include "./components/error-message.html" with message=error %} {% endif %} -
    - diff --git a/tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html b/tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html index 7b40ec45da71dd7016f2a66b2162152eb75b29ad..3d70ae6940025eec1f6eb47dcd54b753beb6f4df 100644 --- a/tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html +++ b/tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html @@ -181,6 +181,14 @@

    {% if not sesion.activo %} + {% if fin_repeticiones %} +
    +

    + Máximo número de repeticiones alcanzado +

    +
    + {% else %} + {% endif %} {% else %}
    {% else %} {% include "../components/error-message.html" with message='Sin calificaciones que mostrar aún' %} diff --git a/tecnicas/templates/tecnicas/manage_vocabulary/create-vocabulary.html b/tecnicas/templates/tecnicas/manage_vocabulary/create-vocabulary.html new file mode 100644 index 0000000000000000000000000000000000000000..0019103df764f6bb4b82ec4b7b187593a69c96a0 --- /dev/null +++ b/tecnicas/templates/tecnicas/manage_vocabulary/create-vocabulary.html @@ -0,0 +1,139 @@ +{% extends 'tecnicas/layouts/base.html' %} +{% load static %} + +{% block title %}Crear vocabulario{% endblock %} + +{% block content %} +
    + + + +
    +

    Crear vocabulario

    + + {% if error %} + {% include "../components/error-message.html" with message=error %} + {% endif %} + {% if message %} + {% include "../components/error-message.html" with message=message %} + {% endif %} + +
    +
    + {% csrf_token %} + + {% if name_vacabulary %} + + {% else %} + + {% endif %} +

    Debajo puede buscar y seleccionar palabras para asignarlas a + este vocabulario.

    +
    + +
    +
    +
    + {% csrf_token %} +

    + Dejar en blaco para buscar las primeras 10 palabras +

    +
    + + +
    +
    + +
      +
      + lupa de busqueda +
      +
    +
    + +
    +
    +

    Lista de palabras

    +
    +
    +
      + {% if not words %} + lista de palabras + {% endif %} + {% for word in words %} +
    • +

      {{ word.nombre_palabra }}

      + +
    • + {% endfor %} +
    +
    +
    + +
    + {% csrf_token %} +

    ¿No encuentras una palabra?

    +
    + + +
    + +
    + + {% if name_vacabulary %} + + {% endif %} + +
    +
    + +
    +
    +{% endblock %} + +{% block extra_js %} + + +{% endblock %} \ No newline at end of file diff --git a/tecnicas/templates/tecnicas/manage_vocabulary/list-vocabulary.html b/tecnicas/templates/tecnicas/manage_vocabulary/list-vocabulary.html new file mode 100644 index 0000000000000000000000000000000000000000..c1539dd3ac92c00de15f1b0f775f66ed66aec47d --- /dev/null +++ b/tecnicas/templates/tecnicas/manage_vocabulary/list-vocabulary.html @@ -0,0 +1,57 @@ +{% extends 'tecnicas/layouts/base.html' %} +{% load static %} + +{% block title %}Panel Sesiones{% endblock %} + +{% block content %} +
    +
    +
    +
    +

    Revisa los vocabularios existentes

    +
    + +
    + {% if message %} + {% include "../components/error-message.html" with message=message %} + {% endif %} + + {% if error %} + {% include "../components/error-message.html" with message=error %} + + {% else %} +
    + {% for vocabu in vocabularies %} + {% include "../components/item_vocabulary.html" with vocabulary=vocabu %} + {% endfor %} +
    + +
    +
    + {% if num_page != 1 %} + + + + {% endif %} + + {% if not last_page %} + + + + {% endif %} +
    +
    + {% endif %} +
    +
    +{% endblock %} \ No newline at end of file diff --git a/tecnicas/templates/tecnicas/manage_vocabulary/panel-vocabulary.html b/tecnicas/templates/tecnicas/manage_vocabulary/panel-vocabulary.html new file mode 100644 index 0000000000000000000000000000000000000000..253a762e83cbaea5f17fa877d3ef8ad329af6363 --- /dev/null +++ b/tecnicas/templates/tecnicas/manage_vocabulary/panel-vocabulary.html @@ -0,0 +1,42 @@ +{% extends 'tecnicas/layouts/base.html' %} +{% load static %} + +{% block title %}Gestionar Catadores{% endblock %} + +{% block content %} + +{% endblock %} \ No newline at end of file diff --git a/tecnicas/urls.py b/tecnicas/urls.py index e160e76e67be47d3ebc0e1e9dbf97dbfc4b8a6a3..f8dee7d53ed3d8462fa2cd671aa22678a52135c2 100644 --- a/tecnicas/urls.py +++ b/tecnicas/urls.py @@ -44,6 +44,18 @@ urlpatterns = [ views.createSession, name="creando_sesion"), + # Gestion de Vocabularios + path("presenter/panel-vocabulario", + views.vocabularyMenu, + name="panel_vocabulario"), + + path("presenter/crear-vocabulario", + views.createVocabulary, + name="crear_vocabulario"), + + path("presenter/lista-vocabulario/", + views.listVocabulary, + name="lista_vocabulario"), # Gestion de catadores path("presenter/panel-catadores", @@ -100,14 +112,18 @@ urlpatterns = [ # APIs - path("api/nueva-etiqueta", + path("presenter/api/nueva-etiqueta", views.newTag, name="nueva_etiqueta"), - path("api/palabras", + path("presenter/api/palabras", views.words, name="api_palabras"), + path("presenter/api/vocabulario//palabras", + views.wordsVocabulary, + name="api_palabras_vocabulary"), + path("testers/api/ratingword", views.reatingWord, name="api_rating_word"), diff --git a/tecnicas/utils/general_controllers.py b/tecnicas/utils/general_controllers.py index ca85a11789101c341018fd35f6a93c3a57dcd3b2..70597537f62c25f8f5faf71c00a2a32ce5e794b8 100644 --- a/tecnicas/utils/general_controllers.py +++ b/tecnicas/utils/general_controllers.py @@ -17,7 +17,7 @@ def getId(value: object | int) -> int | None: def noValidTechnique(params: dict, query_params: dict, name_view: str): if query_params: query_string = urlencode(query_params) - url_redireccion = f"{reverse({name_view}, kwargs=params)}?{query_string}" + url_redireccion = f"{reverse(name_view, kwargs=params)}?{query_string}" else: - url_redireccion = f"{reverse({name_view}, kwargs=params)}" + url_redireccion = f"{reverse(name_view, kwargs=params)}" return redirect(url_redireccion) diff --git a/tecnicas/views/__init__.py b/tecnicas/views/__init__.py index 0209df65da1242514a0a114f1f6a4e3a3e078b3c..517caa7d18822de84228be05eec15659aeff42ce 100644 --- a/tecnicas/views/__init__.py +++ b/tecnicas/views/__init__.py @@ -18,8 +18,13 @@ from .tester_management.tester_create import testerCreate from .tester_management.tester_search import testerSearch from .tester_management.tester_list import testerList +from .vocabulary_management.vocabulry_menu import vocabularyMenu +from .vocabulary_management.create_vocabulary import createVocabulary +from .vocabulary_management.list_vocabulary import listVocabulary + from .apis.api_tag import newTag from .apis.api_words import words +from .apis.api_words import wordsVocabulary from .apis.rating_word import reatingWord from .tester_forms.init_tester_form import initTesterForm diff --git a/tecnicas/views/apis/api_words.py b/tecnicas/views/apis/api_words.py index f24706461de8660a305c440d9cd796e632d799e6..3704833b7d449b6d825429ebbbe9821eab377eff 100644 --- a/tecnicas/views/apis/api_words.py +++ b/tecnicas/views/apis/api_words.py @@ -1,9 +1,10 @@ from django.http import HttpRequest, JsonResponse from tecnicas.decorators import required_presenter -from tecnicas.models import Palabra +from tecnicas.models import Palabra, Vocabulario from tecnicas.utils import general_error from tecnicas.forms.word_form import WordForm + @required_presenter def words(req: HttpRequest): if req.method == "GET": @@ -48,3 +49,21 @@ def words(req: HttpRequest): else: errors = form.errors.get("nombre_palabra") return general_error(errors[0]) + + +@required_presenter +def wordsVocabulary(req: HttpRequest, vocab_id: int): + if req.method == "GET": + vocabulary = Vocabulario.objects.filter(id=vocab_id).first() + if not vocabulary: + return JsonResponse([]) + palabras = vocabulary.palabras.all().values("id", "nombre_palabra") + response_json = { + "ok": True, + "data": { + "words": list(palabras) + } + } + return JsonResponse(response_json) + else: + return JsonResponse({"error": "Este método no es permitido"}) diff --git a/tecnicas/views/apis/rating_word.py b/tecnicas/views/apis/rating_word.py index fd535cea2cb599475f707776f1ed99c0e2348ff1..a142a7dd0823188241e4d7e217bae3b4c8fcea86 100644 --- a/tecnicas/views/apis/rating_word.py +++ b/tecnicas/views/apis/rating_word.py @@ -28,8 +28,8 @@ * Calquier otro metodo que se maneje mandar un error ''' from django.http import HttpRequest, JsonResponse -from ...controllers import ApiRatingController, CalificacionController, DatoController -from ...utils import general_error +from tecnicas.controllers import ApiRatingController, CalificacionController, DatoController +from tecnicas.utils import general_error import json @@ -41,10 +41,11 @@ def reatingWord(req: HttpRequest): received_rating = json.loads(req.POST.get("rating-word")) received_id_word = json.loads(req.POST.get("id-word")) received_id_product = json.loads(req.POST.get("id-product")) + id_technique = json.loads(req.POST.get("id-technique")) view_controller = ApiRatingController( rating_controller=CalificacionController( - technique=req.session["id_technique"], + technique=id_technique, product=received_id_product, tester=req.user.user_catador ), @@ -55,7 +56,7 @@ def reatingWord(req: HttpRequest): ) ) - response_data = view_controller.logicView() + response_data = view_controller.controllPostScales() return JsonResponse(response_data) else: diff --git a/tecnicas/views/sessions_config/configuration_panel_basic.py b/tecnicas/views/sessions_config/configuration_panel_basic.py index aecfe2ad4488daad98eb39ed328e2db0461edd04..d03f2a693a27e9855a01fc10668efdd37d1dceb3 100644 --- a/tecnicas/views/sessions_config/configuration_panel_basic.py +++ b/tecnicas/views/sessions_config/configuration_panel_basic.py @@ -15,6 +15,8 @@ def configurationPanelBasic(req: HttpRequest): response = PanelBasicController.controllGetEscalas(request=req) elif name_tecnica == "rata": response = PanelBasicController.controllGetRATA(request=req) + elif name_tecnica == "cata": + response = PanelBasicController.controllGetCATA(request=req) else: response = redirect( reverse("cata_system:seleccion_tecnica") + "?error=Técnica no valida o sin implementar") @@ -29,6 +31,9 @@ def configurationPanelBasic(req: HttpRequest): elif name_tecnica == "rata": response = PanelBasicController.controllPostRATA( request=req, name_tecnica=name_tecnica) + elif name_tecnica == "cata": + response = PanelBasicController.controllPostCATA( + request=req, name_tecnica=name_tecnica) else: response = redirect( reverse("cata_system:seleccion_tecnica") + "?error=¡Oh, vaya! Cambio de técnica repentino, vuelve a elegir otra vez") diff --git a/tecnicas/views/sessions_config/configuration_panel_codes.py b/tecnicas/views/sessions_config/configuration_panel_codes.py index 44fd87876bfd7d601272ab8c05417b33ed37f2a5..93ff4913e129ab05be1e5549f98e690a823c1a16 100644 --- a/tecnicas/views/sessions_config/configuration_panel_codes.py +++ b/tecnicas/views/sessions_config/configuration_panel_codes.py @@ -12,25 +12,32 @@ def configurationPanelCodes(req: HttpRequest): "?error=datos del formulario requerido no encontrados") data_basic = req.session["form_basic"] + name_technique = data_basic["name_tecnica"] if req.method == "GET": - if data_basic["name_tecnica"] == "escalas": + if name_technique == "escalas": response = PanelCodesController.controllGetEscalas( req, data_basic) - elif data_basic["name_tecnica"] == "rata": + elif name_technique == "rata": response = PanelCodesController.controllGetRATA( req, data_basic) + elif name_technique == "cata": + response = PanelCodesController.controllGetCATA( + req, data_basic) else: response = redirect( reverse("cata_system:seleccion_tecnica") + "?error=Técnica no valida") return response elif req.method == "POST": - if data_basic["name_tecnica"] == "escalas": + if name_technique == "escalas": response = PanelCodesController.controllPostEscalas( req, data_basic) - elif data_basic["name_tecnica"] == "rata": + elif name_technique == "rata": response = PanelCodesController.controllPostRATA(request=req) + elif name_technique == "cata": + response = PanelCodesController.controllPostRATA( + request=req, is_rata=False) else: response = redirect( reverse("cata_system:seleccion_tecnica") + "?error=Técnica no valida") diff --git a/tecnicas/views/sessions_config/configuration_panel_words.py b/tecnicas/views/sessions_config/configuration_panel_words.py index c006e90e7b266ab76cbfc90133f02893a7cb417e..2898f6309b1e933d44e06d1980edc9a95cf6ce66 100644 --- a/tecnicas/views/sessions_config/configuration_panel_words.py +++ b/tecnicas/views/sessions_config/configuration_panel_words.py @@ -1,6 +1,7 @@ from django.http import HttpRequest, JsonResponse from django.shortcuts import redirect from django.urls import reverse +from tecnicas.models import EstiloPalabra from tecnicas.controllers import PanelWordsController from tecnicas.utils import deleteDataSession @@ -12,22 +13,43 @@ def configurationPanelWords(req: HttpRequest): "?error=datos requeridos no encontrados") basic_data = req.session["form_basic"] + name_technique = basic_data["name_tecnica"] + style_words = EstiloPalabra.objects.get( + id=basic_data["estilo_palabras"]).nombre_estilo if req.method == "GET": - if basic_data["name_tecnica"] == "escalas" or basic_data["name_tecnica"] == "rata": - response = PanelWordsController.controllGetEscalas(req) + if name_technique == "escalas" or name_technique == "rata" or name_technique == "cata": + print() + if style_words == "atributos": + response = PanelWordsController.controllGetEscalasAtributes( + req) + elif style_words == "vocabulario": + response = PanelWordsController.controllGetEscalasVocabulary( + req) + else: + response = redirect( + reverse("cata_system:seleccion_tecnica") + "?error=Estilo de palabras no valida") else: response = redirect( reverse("cata_system:seleccion_tecnica") + "?error=Técnica no valida") - return response + elif req.method == "POST": - if basic_data["name_tecnica"] == "escalas" or basic_data["name_tecnica"] == "rata": - response = PanelWordsController.controllPostEscalas(req) + if name_technique == "escalas" or name_technique == "rata" or name_technique == "cata": + if style_words == "atributos": + response = PanelWordsController.controllPostEscalasAtributes( + req) + elif style_words == "vocabulario": + response = PanelWordsController.controllPostEscalasVocabulary( + req) + else: + response = redirect( + reverse("cata_system:seleccion_tecnica") + "?error=Estilo de palabras no valida") else: response = redirect( reverse("cata_system:seleccion_tecnica") + "?error=Técnica no valida") return response + else: return JsonResponse({"message": "Método no permitido"}) diff --git a/tecnicas/views/sessions_config/create_session.py b/tecnicas/views/sessions_config/create_session.py index d81271d4680c6276aa9821bf550c8b96ac654d3b..b9f9371757277bc19918d56da33c33a70938147f 100644 --- a/tecnicas/views/sessions_config/create_session.py +++ b/tecnicas/views/sessions_config/create_session.py @@ -12,9 +12,10 @@ def createSession(req: HttpRequest): "?error=datos requeridos no encontrados") basic_data = req.session["form_basic"] + name_technique = basic_data["name_tecnica"] if req.method == "GET": - if basic_data["name_tecnica"] == "escalas" or basic_data["name_tecnica"] == "rata": + if name_technique == "escalas" or name_technique == "rata" or name_technique == "cata": response = PanelCreateController.controllGetEscalas(req) else: response = redirect( @@ -22,10 +23,12 @@ def createSession(req: HttpRequest): return response if req.method == "POST": - if basic_data["name_tecnica"] == "escalas": + if name_technique == "escalas": response = PanelCreateController.controllPostEscalas(req) - elif basic_data["name_tecnica"] == "rata": + elif name_technique == "rata": response = PanelCreateController.controllPostRATA(req) + elif name_technique == "cata": + response = PanelCreateController.controllPostCATA(req) else: response = redirect( reverse("cata_system:seleccion_tecnica") + "?error=Técnica no valida") diff --git a/tecnicas/views/sessions_management/session_details.py b/tecnicas/views/sessions_management/session_details.py index 7a8371cb2d440eba6975b0dd56dcc01ac9d9e9ac..f292989ee61e4d5964ccfa443e7d0503f350620d 100644 --- a/tecnicas/views/sessions_management/session_details.py +++ b/tecnicas/views/sessions_management/session_details.py @@ -42,7 +42,7 @@ def sessionDetails(req: HttpRequest, session_code: str): if req.POST["action"] == "start_session": response = controller_view.startRepetition( - presenter=req.user.user_presentador) + presenter=req.user.user_presentador, request=req) elif req.POST.get("action") == "delete_session": controller_view.deleteSesorialSession() response = redirect( diff --git a/tecnicas/views/sessions_management/session_monitor.py b/tecnicas/views/sessions_management/session_monitor.py index 12fd4421add590ca3c1404203068a1f6dd41a348..515f26b29af529c2edb747eb6b1e289f2ae22d04 100644 --- a/tecnicas/views/sessions_management/session_monitor.py +++ b/tecnicas/views/sessions_management/session_monitor.py @@ -8,7 +8,7 @@ from django.http import HttpRequest, JsonResponse from django.shortcuts import render, redirect from django.urls import reverse from tecnicas.models import SesionSensorial -from tecnicas.controllers import MonitorEscalasController +from tecnicas.controllers import MonitorEscalasController, MonitorRATAController from tecnicas.utils import noValidTechnique @@ -41,8 +41,19 @@ def sessionMonitor(req: HttpRequest, session_code: str): if use_techinique == "escalas": controll_view = MonitorEscalasController(sensorial_session) action = req.POST["action"] + + if action == "finish_session": + response = controll_view.controllPostFinishSession( + request=req) + else: + response = controll_view.controlGetResponse( + request=req, error="No se ha definido la acción a realizar") + elif use_techinique == "rata": + controll_view = MonitorRATAController(sensorial_session) + action = req.POST["action"] + if action == "finish_session": - response = controll_view.controlPostResponseFinishSession( + response = controll_view.controllPostFinishSession( request=req) else: response = controll_view.controlGetResponse( diff --git a/tecnicas/views/tester_forms/convencional_scales.py b/tecnicas/views/tester_forms/convencional_scales.py index ff8792c6c94841f0ef60706418289b261fcfc27e..7b96ccf80cc28440f313cd18b2978ea4c924b705 100644 --- a/tecnicas/views/tester_forms/convencional_scales.py +++ b/tecnicas/views/tester_forms/convencional_scales.py @@ -55,78 +55,26 @@ - Cata segmento en el que se divide debe tener la etiqueda correspondiente por debajo ''' from django.http import HttpRequest -from django.shortcuts import redirect, render -from django.urls import reverse -from ...controllers import SesionController, PosicionController, CalificacionController, ParticipacionController, PalabrasController, EscalaController, DatoController +from tecnicas.controllers import SesionController, ConvencionalScalesController +from tecnicas.utils import noValidTechnique def convencionalScales(req: HttpRequest, code_sesion: str): - if not "id_order" in req.session: - return redirect(reverse("cata_system:catador_main")) - session = SesionController.getSessionByCode(code_sesion) - technique = session.tecnica - - context = { - "session": session - } - - req.session["id_technique"] = session.tecnica.id + type_technique = session.tecnica.tipo_tecnica.nombre_tecnica if req.method == "GET": - positions = PosicionController.getPostionsInOrder( - id_order=req.session["id_order"]) - - sorted_positions = sorted(positions, key=lambda posi: posi.posicion) - - words = PalabrasController.getWordsInTechnique(technique=technique) - - next_position = CalificacionController.checkProducsWithoutRating( - positions=sorted_positions, - user_cata=req.user.username, - id_technique=session.tecnica.id, - repetition=session.tecnica.repeticion, - technique=technique, - num_words=len(words) - ) - - if isinstance(next_position, dict): - updated_participation = ParticipacionController.finishSession( - req.session["id_participation"]) - return redirect(reverse("cata_system:catador_main")) - - if isinstance(next_position, list): - next_position = next_position[0] - - context["product"] = next_position.id_producto - - ratings_product = CalificacionController.getRatings( - technique=technique, - product=next_position.id_producto, - repetition=technique.repeticion, - user_tester=req.user.username - ) - - if isinstance(ratings_product, dict): - context["error"] = ratings_product["error"] - return render(req, "tecnicas/forms_tester/convencional.html", context) - elif not ratings_product: - context["words"] = words + view_controller = ConvencionalScalesController( + sensorial_session=session, user_tester=req.user.user_catador) + if type_technique == "escalas": + respose = view_controller.controllGetEscalas(request=req) + elif type_technique == "rata": + respose = view_controller.controllGetRATA(request=req) else: - recoreded_data = DatoController.getRerecordedData( - ratings=ratings_product) - if not recoreded_data: - context["words"] = words - else: - words_to_use = PalabrasController.getWordsWithoutData( - recoreded_data=recoreded_data, words=words) - context["words"] = words_to_use - - scale = EscalaController.getScaleByTechnique(technique=technique) - context["scale"] = scale - context["type_scale"] = scale.id_tipo_escala.nombre_escala - - use_tags = EscalaController.getRelatedTagsInScale(scale=scale) - context["tags"] = use_tags + respose = noValidTechnique( + name_view='cata_system:catador_init_session', + params={"code_sesion": session.codigo_sesion}, + query_params={"error": "No es posible poder usar esta técnica"} + ) - return render(req, "tecnicas/forms_tester/convencional.html", context) + return respose diff --git a/tecnicas/views/tester_forms/init_tester_form.py b/tecnicas/views/tester_forms/init_tester_form.py index 0daac8c625b34d0bd531bdbb9248709323e0bc61..6fc17607a93660642331df867e119773d67f22b5 100644 --- a/tecnicas/views/tester_forms/init_tester_form.py +++ b/tecnicas/views/tester_forms/init_tester_form.py @@ -1,61 +1,43 @@ from django.http import HttpRequest, JsonResponse from django.shortcuts import render, redirect from django.urls import reverse -from tecnicas.controllers import MainTesterFormController, ParticipacionController +from tecnicas.controllers import InitSessionTesterController, ParticipacionController from tecnicas.models import SesionSensorial def initTesterForm(req: HttpRequest, code_sesion: str): session = SesionSensorial.objects.get(codigo_sesion=code_sesion) - - context = { - "session": session - } - - view_controller = MainTesterFormController( - code_sesion, req.user.username) - + type_technique = session.tecnica.tipo_tecnica.nombre_tecnica template_url = "tecnicas/forms_tester/init_session.html" - if req.method == "GET": - order = view_controller.checkAndAssignOrder() - - if isinstance(order, dict): - context["error"] = order["error"] - return render(req, template_url, context) - - is_end = view_controller.isEndedSession( - repetition=session.tecnica.repeticion) - - req.session["id_order"] = order.id - context["has_ended"] = is_end + view_controller = InitSessionTesterController( + sensorial_session=session, user_tester=req.user.user_catador) - if is_end: - context["message"] = "El catador ha terminado de realizar su evaluación, espere instrucciones del presentador" + if req.method == "GET": + if type_technique == "escalas": + response = view_controller.controllGetEscalas(request=req) + elif type_technique == "rata": + response = view_controller.controllGetRATA(request=req) + else: + context = { + "session": session, + "error": "La técnica usada en esta sesión o ha sido implementada para ingresar a ella" + } + response = render( + req, template_url, context) - return render(req, template_url, context) + return response elif req.method == "POST": - if req.POST["action"] == "start_posting": - parameters = { - "code_sesion": code_sesion + if type_technique == "escalas" or type_technique == "rata": + response = view_controller.controllPostEscalas(request=req) + else: + context = { + "session": session, + "error": "Esta opción aun no esta disponible para la técnica usada por la sesión" } + response = render( + req, template_url, context) - update_participation = ParticipacionController.enterSession( - tester=req.user.user_catador, session=session) - if isinstance(update_participation, dict): - context["error"] = update_participation["error"] - return render(req, template_url, context) - - req.session["id_participation"] = update_participation.id - return redirect(reverse("cata_system:session_convencional", kwargs=parameters)) - elif req.POST["action"] == "exit_session": - response = ParticipacionController.outSession( - tester=req.user.user_catador, session=session) - if isinstance(response, dict): - context["error"] = response["error"] - return render(req, template_url, context) - else: - context["error"] = "Acción sin especificar" - return render(req, template_url, context) + return response else: return JsonResponse({"error": "metodo no permitido"}) diff --git a/tecnicas/views/tester_forms/login_session.py b/tecnicas/views/tester_forms/login_session.py index cf2d9f2a679748367bbeea1ede3c10acfd4f4d0f..94f004ef86a99337aa43b37e12897072ecd1981f 100644 --- a/tecnicas/views/tester_forms/login_session.py +++ b/tecnicas/views/tester_forms/login_session.py @@ -2,7 +2,7 @@ from django.http import HttpRequest, JsonResponse from django.shortcuts import render, redirect from django.urls import reverse from tecnicas.utils import general_error -from tecnicas.controllers import LoginTesterController +from tecnicas.controllers import LoginSessionTesterController def loginSessionTester(req: HttpRequest): @@ -14,7 +14,7 @@ def loginSessionTester(req: HttpRequest): if not tester_user or not session_code: return general_error("Se esperan credenciales") - login_controller = LoginTesterController() + login_controller = LoginSessionTesterController() existCredentials = login_controller.existCredential( tester_user, session_code) @@ -28,12 +28,12 @@ def loginSessionTester(req: HttpRequest): type_technique = session.tecnica.tipo_tecnica.nombre_tecnica if type_technique == "escalas": - response = login_controller.validateEntryEscalas() + response = login_controller.validateEntryEscalas(request=req) elif type_technique == "rata": - response = login_controller.validateEntryRATA() + response = login_controller.validateEntryRATA(request=req) else: context = { - "error": "La técnica usada en esta sesión o ha sido implementada para ingresar a ella" + "error": "La técnica usada en esta sesión es invalida o no ha sido implementada para ingresar a ella" } response = render( req, "tecnicas/forms_tester/login_session.html", context) diff --git a/tecnicas/views/vocabulary_management/create_vocabulary.py b/tecnicas/views/vocabulary_management/create_vocabulary.py new file mode 100644 index 0000000000000000000000000000000000000000..a3ddb6e1d71621a1be673cb4b736016129adc78a --- /dev/null +++ b/tecnicas/views/vocabulary_management/create_vocabulary.py @@ -0,0 +1,19 @@ +from django.shortcuts import render +from django.http import HttpRequest +from tecnicas.forms import WordForm +from tecnicas.controllers import CreateVocabularyController + + +def createVocabulary(req: HttpRequest): + view_controller = CreateVocabularyController() + if req.method == "GET": + response = view_controller.controllGet(req) + return response + + elif req.method == "POST": + response = view_controller.controllPost(req) + return response + + else: + context = {"error": "Método no permitido"} + return render(req, "tecnicas/manage_vocabulary/create-vocabulary.html", context) diff --git a/tecnicas/views/vocabulary_management/list_vocabulary.py b/tecnicas/views/vocabulary_management/list_vocabulary.py new file mode 100644 index 0000000000000000000000000000000000000000..8b203e7d4460ca9b2c8667a7ba1a440627d0ebcb --- /dev/null +++ b/tecnicas/views/vocabulary_management/list_vocabulary.py @@ -0,0 +1,11 @@ +from django.http import HttpRequest +from tecnicas.controllers import ListVocabularyController + + +def listVocabulary(req: HttpRequest, num_page: int): + controll_view = ListVocabularyController() + if req.method == "GET": + response = controll_view.controllGet(req, num_page) + return response + else: + pass diff --git a/tecnicas/views/vocabulary_management/vocabulry_menu.py b/tecnicas/views/vocabulary_management/vocabulry_menu.py new file mode 100644 index 0000000000000000000000000000000000000000..f2a175ef274c1738f16cd723ce6da7e7a37aee67 --- /dev/null +++ b/tecnicas/views/vocabulary_management/vocabulry_menu.py @@ -0,0 +1,4 @@ +from django.shortcuts import render + +def vocabularyMenu(req): + return render(req, "tecnicas/manage_vocabulary/panel-vocabulary.html") \ No newline at end of file