chartManD commited on
Commit
4607b36
·
1 Parent(s): a067a65

Monitoero con PF y controlador para terminar sesion

Browse files
tecnicas/controllers/__init__.py CHANGED
@@ -23,9 +23,11 @@ from .views_controller.session_management.details_escala_controller import Detal
23
  from .views_controller.session_management.details_rata_controller import DetallesRATAController
24
  from .views_controller.session_management.details_cata_controller import DetallesCATAController
25
  from .views_controller.session_management.details_pf_controller import DetallesPFController
 
26
  from .views_controller.session_management.monitor_controller import MonitorController
27
  from .views_controller.session_management.monitor_escalas_controller import MonitorEscalasController
28
  from .views_controller.session_management.monitor_rata_controller import MonitorRATAController
 
29
 
30
  from .views_controller.sessions_tester.login_session_tester_controller import LoginSessionTesterController
31
  from .views_controller.sessions_tester.init_session_tester_controller import InitSessionTesterController
 
23
  from .views_controller.session_management.details_rata_controller import DetallesRATAController
24
  from .views_controller.session_management.details_cata_controller import DetallesCATAController
25
  from .views_controller.session_management.details_pf_controller import DetallesPFController
26
+
27
  from .views_controller.session_management.monitor_controller import MonitorController
28
  from .views_controller.session_management.monitor_escalas_controller import MonitorEscalasController
29
  from .views_controller.session_management.monitor_rata_controller import MonitorRATAController
30
+ from .views_controller.session_management.monitor_pf_controller import MonitorPFController
31
 
32
  from .views_controller.sessions_tester.login_session_tester_controller import LoginSessionTesterController
33
  from .views_controller.sessions_tester.init_session_tester_controller import InitSessionTesterController
tecnicas/controllers/views_controller/session_management/details_pf_controller.py CHANGED
@@ -1,4 +1,8 @@
1
- from tecnicas.models import SesionSensorial
 
 
 
 
2
  from .details_controller import DetallesController
3
 
4
 
@@ -28,6 +32,36 @@ class DetallesPFController(DetallesController):
28
 
29
  return self.context
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  def getStatus(self, rep: int, activate: bool):
32
  status = ""
33
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import SesionSensorial, Presentador, Participacion
5
+ from tecnicas.controllers import ParticipacionController
6
  from .details_controller import DetallesController
7
 
8
 
 
32
 
33
  return self.context
34
 
35
+ def startRepetition(self, presenter: Presentador, request: HttpRequest):
36
+ creator = presenter
37
+ technique = self.session.tecnica
38
+
39
+ if creator.user.username != self.session.creadoPor.user.username:
40
+ return self.getResponse(error="Solo el presentador que crea la sesión puede iniciar la repetición o fase", request=request)
41
+ elif self.session.activo:
42
+ return self.getResponse(error="La sesión ya está activada", request=request)
43
+
44
+ there_participacions = Participacion.objects.filter(
45
+ tecnica=technique).exists()
46
+
47
+ if there_participacions:
48
+ (is_update_participations,
49
+ message) = ParticipacionController.outAllInSession(self.session)
50
+ if not is_update_participations:
51
+ return self.getResponse(error=message, request=request)
52
+
53
+ self.session.activo = True
54
+ technique.repeticion = technique.repeticion + 1
55
+
56
+ technique.save()
57
+ self.session.save()
58
+
59
+ parameters = {
60
+ "session_code": self.session.codigo_sesion
61
+ }
62
+ return redirect(
63
+ reverse(self.url_next, kwargs=parameters))
64
+
65
  def getStatus(self, rep: int, activate: bool):
66
  status = ""
67
 
tecnicas/controllers/views_controller/session_management/monitor_escalas_controller.py CHANGED
@@ -1,6 +1,3 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import render, redirect
3
- from django.urls import reverse
4
  from tecnicas.models import Dato, Participacion
5
  from tecnicas.controllers import SesionController
6
  from .monitor_controller import MonitorController
 
 
 
 
1
  from tecnicas.models import Dato, Participacion
2
  from tecnicas.controllers import SesionController
3
  from .monitor_controller import MonitorController
tecnicas/controllers/views_controller/session_management/monitor_pf_controller.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from tecnicas.models import Dato, Participacion, Catador, ListaPalabras, Producto
5
+ from tecnicas.controllers import SesionController
6
+ from .monitor_controller import MonitorController
7
+
8
+
9
+ class MonitorPFController(MonitorController):
10
+ def __init__(self, session: SesionController):
11
+ super().__init__(session)
12
+ self.url_view = "tecnicas/manage_sesions/monitor-session-pf.html"
13
+ self.previus_view = "cata_system:detalles_sesion"
14
+
15
+ def checkAllFinish(self):
16
+ rep = self.sensorial_session.tecnica.repeticion
17
+
18
+ finish_data = ()
19
+
20
+ if rep == 1 or rep == 2:
21
+ finish_data = self.checkFinishFirstPhase()
22
+ elif rep >= 3:
23
+ finish_data = self.checkFinishRepetition()
24
+
25
+ return finish_data
26
+
27
+ def checkFinishFirstPhase(self):
28
+ num_paricipations = Participacion.objects.filter(
29
+ tecnica=self.sensorial_session.tecnica).count()
30
+ if num_paricipations < self.sensorial_session.tecnica.limite_catadores:
31
+ return (False, "No se ha alcanzado el número máximo de catadores")
32
+
33
+ unfinished_participations = Participacion.objects.filter(
34
+ tecnica=self.sensorial_session.tecnica, finalizado=False).count()
35
+ if not unfinished_participations or len(unfinished_participations) > 0:
36
+ return (False, "No todos los catadores han finalizado su evaluación")
37
+
38
+ return (True, "Puedes finalizar la sesión")
39
+
40
+ def checkFinishRepetition(self) -> tuple[bool, str]:
41
+ technique = self.sensorial_session.tecnica
42
+
43
+ # Revisar numero de catadores sea alcanzado
44
+ all_participations = list(
45
+ Participacion.objects.filter(tecnica=technique))
46
+ if len(all_participations) < technique.limite_catadores:
47
+ return (False, "No se ha alcanzado el número máximo de Catadores")
48
+
49
+ # Revisar que cada catador haya terminado de calificar sus palabras
50
+ for particiapation in all_participations:
51
+ expected_ratings_repetition = self.getExpectedRatings(
52
+ tester=particiapation.catador)
53
+
54
+ num_ratings_now = Dato.objects.filter(
55
+ id_calificacion__num_repeticion=technique.repeticion,
56
+ id_calificacion__id_catador=particiapation.catador,
57
+ id_calificacion__id_tecnica=technique
58
+ ).count()
59
+
60
+ if num_ratings_now < expected_ratings_repetition:
61
+ return (False, "No todos los catadores han finalizado su evaluación")
62
+
63
+ return (True, "Puedes finalizar la sesión")
64
+
65
+ def getExpectedRatings(self, tester: Catador):
66
+ num_words = ListaPalabras.objects.get(
67
+ tecnica=self.sensorial_session.tecnica,
68
+ catador=tester,
69
+ es_final=True
70
+ ).palabras.all().count()
71
+
72
+ num_products = Producto.objects.filter(
73
+ id_tecnica=self.sensorial_session.tecnica).count()
74
+
75
+ return num_products * num_words
tecnicas/controllers/views_controller/session_management/monitor_rata_controller.py CHANGED
@@ -1,6 +1,3 @@
1
- from django.http import HttpRequest
2
- from django.shortcuts import render, redirect
3
- from django.urls import reverse
4
  from tecnicas.models import Dato, Participacion
5
  from tecnicas.controllers import SesionController
6
  from .monitor_controller import MonitorController
 
 
 
 
1
  from tecnicas.models import Dato, Participacion
2
  from tecnicas.controllers import SesionController
3
  from .monitor_controller import MonitorController
tecnicas/migrations/0022_alter_sesionsensorial_codigo_sesion.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-11-12 20:16
2
+
3
+ import shortuuid.main
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('tecnicas', '0021_rename_nomre_vocabulario_vocabulario_nombre_vocabulario_and_more'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name='sesionsensorial',
16
+ name='codigo_sesion',
17
+ field=models.CharField(default=shortuuid.main.ShortUUID.uuid, editable=False, max_length=22, primary_key=True, serialize=False),
18
+ ),
19
+ ]
tecnicas/migrations/0023_alter_sesionsensorial_codigo_sesion_listapalabras.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-11-12 20:24
2
+
3
+ import django.db.models.deletion
4
+ import shortuuid.main
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('tecnicas', '0022_alter_sesionsensorial_codigo_sesion'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AlterField(
16
+ model_name='sesionsensorial',
17
+ name='codigo_sesion',
18
+ field=models.CharField(default=shortuuid.main.ShortUUID.uuid, editable=False, max_length=22, primary_key=True, serialize=False),
19
+ ),
20
+ migrations.CreateModel(
21
+ name='ListaPalabras',
22
+ fields=[
23
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24
+ ('es_final', models.BooleanField(default=False)),
25
+ ('catador', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='catador_lista', to='tecnicas.catador')),
26
+ ('palabras', models.ManyToManyField(related_name='lista_palabras', to='tecnicas.palabra')),
27
+ ('tecnica', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tecnica_lista', to='tecnicas.tecnica')),
28
+ ],
29
+ ),
30
+ ]
tecnicas/models/__init__.py CHANGED
@@ -27,4 +27,6 @@ from .dato_valor import ValorBooleano
27
  from .orden import Orden
28
  from .orden import Posicion
29
 
30
- from .participacion import Participacion
 
 
 
27
  from .orden import Orden
28
  from .orden import Posicion
29
 
30
+ from .participacion import Participacion
31
+
32
+ from .lista_palabras import ListaPalabras
tecnicas/models/lista_palabras.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.db import models
2
+ from tecnicas.models import Tecnica, Catador, Palabra
3
+
4
+
5
+ class ListaPalabras(models.Model):
6
+ tecnica = models.ForeignKey(
7
+ Tecnica, on_delete=models.CASCADE, related_name="tecnica_lista")
8
+ catador = models.ForeignKey(
9
+ Catador, on_delete=models.CASCADE, related_name="catador_lista")
10
+ es_final = models.BooleanField(default=False)
11
+ palabras = models.ManyToManyField(
12
+ Palabra, related_name="lista_palabras")
13
+
14
+ def __str__(self):
15
+ return f"{self.tecnica.codigo_sesion} - {self.catador.user.username}"
tecnicas/templates/tecnicas/manage_sesions/monitor-session-pf.html ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'tecnicas/layouts/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title %}Monitoreo{% endblock %}
5
+
6
+ {% block content %}
7
+ <article class="cts-container-main">
8
+ <article class="cts-wrap-content">
9
+ <header class="text-center">
10
+ <h1 class="text-3xl font-bold text-black">Sesión sensorial en curso</h1>
11
+ </header>
12
+
13
+ <section class="flex flex-col sm:flex-row justify-between items-center gap-10">
14
+ <p class="text-xl text-center bg-surface-card p-4 text-black rounded-lg shadow-lg">
15
+ Código de sesión:<br>
16
+ <span class="font-mono text-2xl font-bold">{{ code_session }}</span>
17
+ </p>
18
+ <div class="flex flex-col gap-2">
19
+ <button class="uppercase cts-btn-general-compress cts-btn-secondary py-2 px-6 btn-push"
20
+ onclick="finishSession()">
21
+ Finalizar sesión
22
+ </button>
23
+ <a href="{% url 'cata_system:detalles_sesion' session_code=code_session %}" class="w-full">
24
+ <button class="uppercase cts-btn-general-compress cts-btn-error py-2 px-6 btn-push w-full">
25
+ Regresar
26
+ </button>
27
+ </a>
28
+ </div>
29
+ </section>
30
+
31
+ <form action="" method="post" class="hidden action-form">
32
+ <input type="hidden" name="action">
33
+ {% csrf_token %}
34
+ </form>
35
+
36
+ {% if error %}
37
+ {% include "../components/error-message.html" with message=error %}
38
+ {% endif %}
39
+
40
+ {% if message %}
41
+ {% include "../components/error-message.html" with message=message %}
42
+ {% endif %}
43
+
44
+ <section aria-labelledby="catadores-titulo">
45
+ <article
46
+ class="flex max-sm:flex-col justify-around bg-surface-card border border-gray-300 rounded-md p-3 mb-4 items-center shadow-lg">
47
+ <button class="uppercase cts-btn-general-compress cts-btn-tertiary py-2 px-6 btn-push"
48
+ onclick="reloadPage()">
49
+ Actualizar lista
50
+ </button>
51
+ <h2 id="catadores-titulo" class="text-2xl text-center font-bold rounded-lg text-gray-700 py-2 px-3">
52
+ Catadores activos
53
+ </h2>
54
+ </article>
55
+
56
+ <article
57
+ class="flex flex-col justify-center gap-3 bg-surface-card border border-gray-300 rounded-md p-3 mb-4 text-lg max-sm:text-sm text-gray-700 shadow-lg">
58
+ <div class="flex justify-center items-center">
59
+ <h2 class="text-xl font-semibold">Participantes</h2>
60
+ </div>
61
+ <section class="flex flex-wrap justify-around items-center">
62
+ <div class="flex flex-col sm:flex-row sm:items-center sm:gap-2">
63
+ <span class="font-semibold">Máximo:</span>
64
+ <span>{{ max_testers }}</span>
65
+ </div>
66
+ <div class="flex flex-col sm:flex-row sm:items-center sm:gap-2">
67
+ <span class="font-semibold">
68
+ Actuales:
69
+ </span>
70
+ <span>{{ current_testers }}</span>
71
+ </div>
72
+ <div class="flex flex-col sm:flex-row sm:items-center sm:gap-2">
73
+ <span class="font-semibold">Activos:</span>
74
+ <span class="text-green-600 font-semibold">{{ active_testers }}</span>
75
+ </div>
76
+ </section>
77
+ </article>
78
+
79
+ <article class="max-sm:overflow-x-auto shadow-lg">
80
+ <div class="min-w-[400px] sm:min-w-0">
81
+ <div
82
+ class="grid grid-cols-4 bg-surface-sweet text-center font-semibold text-black text-sm rounded-t-lg max-sm:w-full [&_>*]:px-2">
83
+ <div class="py-2 border-r border-gray-400">Usuario</div>
84
+ <div class="py-2 border-r border-gray-400">Nombre</div>
85
+ <div class="py-2 border-r border-gray-400">Estado</div>
86
+ <div class="py-2">Finalizado</div>
87
+ </div>
88
+
89
+ <ul class="divide-y divide-gray-400 max-sm:w-full text-black">
90
+ {% for parti in participations %}
91
+ <li class="grid grid-cols-4 text-center bg-surface-card py-2 [&_>*]:px-2">
92
+ <p class="border-r border-gray-400">{{ parti.catador.user.username }}</p>
93
+ <p class="border-r border-gray-400">
94
+ {{ parti.catador.user.first_name }}
95
+ {{ parti.catador.user.last_name}}
96
+ </p>
97
+
98
+ {% if parti.activo %}
99
+ <p class="border-r border-gray-400 text-green-600 font-semibold">Activo</p>
100
+ {% else %}
101
+ <p class="border-r border-gray-400 text-red-500 font-semibold">No activo</p>
102
+ {% endif %}
103
+
104
+ {% if parti.finalizado %}
105
+ <p class="text-green-600 font-semibold">Si</p>
106
+ {% else %}
107
+ <p class="text-red-500 font-semibold">No</p>
108
+ {% endif %}
109
+ </li>
110
+ {% endfor %}
111
+ </ul>
112
+ </div>
113
+ </article>
114
+ </section>
115
+ </article>
116
+ </article>
117
+ {% endblock %}
118
+
119
+ {% block extra_js %}
120
+ <script>
121
+ function reloadPage () {
122
+ location.reload()
123
+ }
124
+ </script>
125
+ <script src="{% static 'js/finish-session.js' %}"></script>
126
+ {% endblock %}
tecnicas/views/sessions_management/session_details.py CHANGED
@@ -55,16 +55,21 @@ def sessionDetails(req: HttpRequest, session_code: str):
55
  if req.POST["action"] == "start_session":
56
  response = controller_view.startRepetition(
57
  presenter=req.user.user_presentador, request=req)
 
58
  elif req.POST.get("action") == "delete_session":
59
  controller_view.deleteSesorialSession()
60
  response = redirect(
61
  reverse("cata_system:panel_sesiones", kwargs={"page": 1}))
 
62
  else:
63
  response = controller_view.getResponse(
64
  error="No se reconoce la acción a realizar")
 
65
  elif use_techinique == "perfil flash":
66
- response = JsonResponse(
67
- {"message": "Estoy trabajando en la funcionalidad que eligio"})
 
 
68
  else:
69
  response = noValidTechnique()
70
 
 
55
  if req.POST["action"] == "start_session":
56
  response = controller_view.startRepetition(
57
  presenter=req.user.user_presentador, request=req)
58
+
59
  elif req.POST.get("action") == "delete_session":
60
  controller_view.deleteSesorialSession()
61
  response = redirect(
62
  reverse("cata_system:panel_sesiones", kwargs={"page": 1}))
63
+
64
  else:
65
  response = controller_view.getResponse(
66
  error="No se reconoce la acción a realizar")
67
+
68
  elif use_techinique == "perfil flash":
69
+ controller_view = DetallesPFController(session=sensorial_session)
70
+ response = controller_view.startRepetition(
71
+ presenter=req.user.user_presentador, request=req)
72
+
73
  else:
74
  response = noValidTechnique()
75
 
tecnicas/views/sessions_management/session_monitor.py CHANGED
@@ -3,12 +3,11 @@ Para finalizar la sesion se debe realizar lo siguiente
3
  # Obtener todas las participaciones
4
 
5
  '''
6
-
7
  from django.http import HttpRequest, JsonResponse
8
  from django.shortcuts import render, redirect
9
  from django.urls import reverse
10
  from tecnicas.models import SesionSensorial
11
- from tecnicas.controllers import MonitorEscalasController, MonitorRATAController
12
  from tecnicas.utils import noValidTechnique
13
 
14
 
@@ -21,6 +20,11 @@ def sessionMonitor(req: HttpRequest, session_code: str):
21
  if use_techinique == "escalas" or use_techinique == "rata" or use_techinique == "cata":
22
  controll_view = MonitorEscalasController(sensorial_session)
23
  response = controll_view.controllGetResponse(request=req)
 
 
 
 
 
24
  else:
25
  response = noValidTechnique(
26
  params={
@@ -47,6 +51,7 @@ def sessionMonitor(req: HttpRequest, session_code: str):
47
  else:
48
  response = controll_view.controlGetResponse(
49
  request=req, error="No se ha definido la acción a realizar")
 
50
  elif use_techinique == "rata" or use_techinique == "cata":
51
  controll_view = MonitorRATAController(sensorial_session)
52
  action = req.POST["action"]
@@ -57,6 +62,18 @@ def sessionMonitor(req: HttpRequest, session_code: str):
57
  else:
58
  response = controll_view.controlGetResponse(
59
  request=req, error="No se ha definido la acción a realizar")
 
 
 
 
 
 
 
 
 
 
 
 
60
  else:
61
  response = noValidTechnique(
62
  params={
 
3
  # Obtener todas las participaciones
4
 
5
  '''
 
6
  from django.http import HttpRequest, JsonResponse
7
  from django.shortcuts import render, redirect
8
  from django.urls import reverse
9
  from tecnicas.models import SesionSensorial
10
+ from tecnicas.controllers import MonitorEscalasController, MonitorRATAController, MonitorPFController
11
  from tecnicas.utils import noValidTechnique
12
 
13
 
 
20
  if use_techinique == "escalas" or use_techinique == "rata" or use_techinique == "cata":
21
  controll_view = MonitorEscalasController(sensorial_session)
22
  response = controll_view.controllGetResponse(request=req)
23
+
24
+ elif use_techinique == "perfil flash":
25
+ controll_view = MonitorPFController(sensorial_session)
26
+ response = controll_view.controllGetResponse(request=req)
27
+
28
  else:
29
  response = noValidTechnique(
30
  params={
 
51
  else:
52
  response = controll_view.controlGetResponse(
53
  request=req, error="No se ha definido la acción a realizar")
54
+
55
  elif use_techinique == "rata" or use_techinique == "cata":
56
  controll_view = MonitorRATAController(sensorial_session)
57
  action = req.POST["action"]
 
62
  else:
63
  response = controll_view.controlGetResponse(
64
  request=req, error="No se ha definido la acción a realizar")
65
+
66
+ elif use_techinique == "perfil flash":
67
+ controll_view = MonitorPFController(sensorial_session)
68
+ action = req.POST["action"]
69
+
70
+ if action == "finish_session":
71
+ response = controll_view.controllPostFinishSession(
72
+ request=req)
73
+ else:
74
+ response = controll_view.controlGetResponse(
75
+ request=req, error="No se ha definido la acción a realizar")
76
+
77
  else:
78
  response = noValidTechnique(
79
  params={