Norberto Montalvo García commited on
Commit
aba686b
·
unverified ·
2 Parent(s): db9b8bc 0a769dc

Merge pull request #3 from CascoArcilla/HU8

Browse files
Files changed (35) hide show
  1. cata_system/settings.py +6 -0
  2. tecnicas/admin.py +2 -0
  3. tecnicas/controllers/__init__.py +3 -1
  4. tecnicas/controllers/detalles_sesion_controller.py +8 -5
  5. tecnicas/controllers/login_tester_controller.py +62 -0
  6. tecnicas/controllers/main_tester_form_controller.py +48 -0
  7. tecnicas/controllers/presentador_controller.py +14 -0
  8. tecnicas/controllers/sesion_controller.py +28 -7
  9. tecnicas/controllers/tecnica_controller.py +8 -0
  10. tecnicas/middlewares/__init__.py +1 -0
  11. tecnicas/middlewares/login_tester_middleware.py +19 -0
  12. tecnicas/migrations/0015_alter_sesionsensorial_codigo_sesion.py +19 -0
  13. tecnicas/migrations/0016_alter_sesionsensorial_codigo_sesion_participacion.py +30 -0
  14. tecnicas/migrations/0017_alter_sesionsensorial_codigo_sesion_and_more.py +22 -0
  15. tecnicas/migrations/0018_alter_sesionsensorial_codigo_sesion_participacion.py +30 -0
  16. tecnicas/models/__init__.py +3 -1
  17. tecnicas/models/participacion.py +15 -0
  18. tecnicas/models/sesion_sensorial.py +1 -1
  19. tecnicas/pato.py +0 -42
  20. tecnicas/static/img/check.svg +4 -0
  21. tecnicas/static/img/exit.svg +12 -0
  22. tecnicas/static/img/monitor.svg +12 -0
  23. tecnicas/static/js/details-session.js +1 -1
  24. tecnicas/static/js/start-tester-test.js +4 -0
  25. tecnicas/templates/tecnicas/cata-login.html +28 -7
  26. tecnicas/templates/tecnicas/forms_tester/main_tester.html +118 -0
  27. tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html +16 -0
  28. tecnicas/urls.py +9 -4
  29. tecnicas/utils/__init__.py +1 -0
  30. tecnicas/utils/shuffle_arrays.py +7 -0
  31. tecnicas/views/__init__.py +3 -1
  32. tecnicas/views/login_catador.py +0 -4
  33. tecnicas/views/login_tester.py +36 -0
  34. tecnicas/views/sessions_management/session_details.py +3 -3
  35. tecnicas/views/tester_forms/main_tester_form.py +33 -0
cata_system/settings.py CHANGED
@@ -32,6 +32,11 @@ DEBUG = True
32
 
33
  ALLOWED_HOSTS = []
34
 
 
 
 
 
 
35
 
36
  # Application definition
37
 
@@ -65,6 +70,7 @@ MIDDLEWARE = [
65
 
66
  'django_browser_reload.middleware.BrowserReloadMiddleware',
67
 
 
68
  ]
69
 
70
  ROOT_URLCONF = 'cata_system.urls'
 
32
 
33
  ALLOWED_HOSTS = []
34
 
35
+ # SESSIONS TIMEOUT
36
+ SESSION_COOKIE_AGE = 30 * 60
37
+
38
+ SESSION_EXPIRE_AT_BROWSER_CLOSE = True
39
+
40
 
41
  # Application definition
42
 
 
70
 
71
  'django_browser_reload.middleware.BrowserReloadMiddleware',
72
 
73
+ 'tecnicas.middlewares.LoginTesterMiddleware'
74
  ]
75
 
76
  ROOT_URLCONF = 'cata_system.urls'
tecnicas/admin.py CHANGED
@@ -20,6 +20,7 @@ from .models import Escala
20
  from .models import EtiquetasEscala
21
 
22
  from .models import Producto
 
23
 
24
  # Register your models here.
25
  admin.site.register(CategoriaTecnica)
@@ -41,3 +42,4 @@ admin.site.register(Escala)
41
  admin.site.register(EtiquetasEscala)
42
 
43
  admin.site.register(Producto)
 
 
20
  from .models import EtiquetasEscala
21
 
22
  from .models import Producto
23
+ from .models import Participacion
24
 
25
  # Register your models here.
26
  admin.site.register(CategoriaTecnica)
 
42
  admin.site.register(EtiquetasEscala)
43
 
44
  admin.site.register(Producto)
45
+ admin.site.register(Participacion)
tecnicas/controllers/__init__.py CHANGED
@@ -7,4 +7,6 @@ from .estilo_palabras_controller import EstiloPalabrasController
7
  from .palabras_controller import PalabrasController
8
  from .sesion_controller import SesionController
9
  from .calificacion_controller import CalificacionController
10
- from .detalles_sesion_controller import DetallesSesionController
 
 
 
7
  from .palabras_controller import PalabrasController
8
  from .sesion_controller import SesionController
9
  from .calificacion_controller import CalificacionController
10
+ from .detalles_sesion_controller import DetallesSesionController
11
+ from .login_tester_controller import LoginTesterController
12
+ from .main_tester_form_controller import MainTesterFormController
tecnicas/controllers/detalles_sesion_controller.py CHANGED
@@ -1,4 +1,4 @@
1
- from ..models import SesionSensorial, Presentador
2
  from . import CalificacionController, PalabrasController
3
  from ..utils import controller_error
4
 
@@ -25,22 +25,25 @@ class DetallesSesionController():
25
  try:
26
  creator = Presentador.objects.get(nombre_usuario=username)
27
  session = SesionSensorial.objects.get(codigo_sesion=session_code)
 
28
  except Presentador.DoesNotExist:
29
  return controller_error("no existe presentador")
30
  except SesionSensorial.DoesNotExist:
31
  return controller_error("no existe sesión sensorial")
 
 
32
 
33
  if creator.nombre_usuario != session.creadoPor.nombre_usuario:
34
  return controller_error("solo el presentador que crea la sesión puede iniciar la repetición")
35
  elif session.activo:
36
  return controller_error("la sesión ya está activada")
37
- elif session.tecnica.repecion == session.tecnica.repeticiones_max:
38
  return controller_error("se ha alcanzado el número de repeticiones máxima")
39
 
40
  session.activo = True
41
- rep = session.tecnica.repecion
42
- session.tecnica.repecion = rep + 1
43
 
 
44
  session.save()
45
-
46
  return session
 
1
+ from ..models import SesionSensorial, Presentador, Tecnica
2
  from . import CalificacionController, PalabrasController
3
  from ..utils import controller_error
4
 
 
25
  try:
26
  creator = Presentador.objects.get(nombre_usuario=username)
27
  session = SesionSensorial.objects.get(codigo_sesion=session_code)
28
+ technique = Tecnica.objects.get(id=session.tecnica.id)
29
  except Presentador.DoesNotExist:
30
  return controller_error("no existe presentador")
31
  except SesionSensorial.DoesNotExist:
32
  return controller_error("no existe sesión sensorial")
33
+ except Tecnica.DoesNotExist:
34
+ return controller_error("Ha ocurrido un error al recuperar la técnica")
35
 
36
  if creator.nombre_usuario != session.creadoPor.nombre_usuario:
37
  return controller_error("solo el presentador que crea la sesión puede iniciar la repetición")
38
  elif session.activo:
39
  return controller_error("la sesión ya está activada")
40
+ elif technique.repecion == technique.repeticiones_max:
41
  return controller_error("se ha alcanzado el número de repeticiones máxima")
42
 
43
  session.activo = True
44
+ technique.repecion = technique.repecion + 1
 
45
 
46
+ technique.save()
47
  session.save()
48
+
49
  return session
tecnicas/controllers/login_tester_controller.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..models import Catador, SesionSensorial, Participacion
2
+ from ..utils import controller_error
3
+ from django.db import transaction
4
+
5
+
6
+ class LoginTesterController():
7
+ tester: Catador
8
+ session: SesionSensorial
9
+ taster_participation: Participacion
10
+
11
+ def __init__(self):
12
+ self.tester = Catador()
13
+ self.session = SesionSensorial()
14
+
15
+ def existCredential(self, user_tester: str, code_session: str):
16
+ try:
17
+ self.tester = Catador.objects.get(usuarioCatador=user_tester)
18
+ self.session = SesionSensorial.objects.get(
19
+ codigo_sesion=code_session)
20
+
21
+ return True
22
+ except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
23
+ return controller_error("Credenciales inválidas")
24
+
25
+ def validateEntry(self):
26
+ if not self.tester.nombre or not self.session.codigo_sesion:
27
+ return controller_error("Credenciales no definidas")
28
+
29
+ if not self.session.activo:
30
+ return controller_error("La sesión no está activa actualmente")
31
+
32
+ if self.session.tecnica.repecion > 1:
33
+ try:
34
+ self.taster_participation = Participacion.objects.get(
35
+ tecnica=self.session.tecnica, catador=self.tester)
36
+ return self.taster_participation
37
+ except Participacion.DoesNotExist:
38
+ return controller_error("No tienes permitido entrar a esta sesión")
39
+ else:
40
+ try:
41
+ self.taster_participation = Participacion.objects.get(
42
+ tecnica=self.session.tecnica, catador=self.tester)
43
+ return self.taster_participation
44
+ except Participacion.DoesNotExist:
45
+ with transaction.atomic():
46
+ code_session = self.session.codigo_sesion
47
+ self.session = SesionSensorial.objects.select_for_update().get(
48
+ codigo_sesion=code_session)
49
+
50
+ max_testers = self.session.tecnica.limite_catadores
51
+ current_num_testers = Participacion.objects.filter(
52
+ tecnica=self.session.tecnica).count()
53
+
54
+ if current_num_testers >= max_testers:
55
+ return controller_error("La sesión ha alcanzado el número máximo de catadores")
56
+
57
+ self.taster_participation = Participacion.objects.create(
58
+ tecnica=self.session.tecnica,
59
+ catador=self.tester
60
+ )
61
+
62
+ return self.taster_participation
tecnicas/controllers/main_tester_form_controller.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..models import Catador, SesionSensorial, Orden
2
+ from ..utils import controller_error, shuffleArray
3
+ from django.db import transaction
4
+
5
+
6
+ class MainTesterFormController():
7
+ tester: Catador
8
+ session: SesionSensorial
9
+ order: Orden | dict
10
+
11
+ def __init__(self, code_session: str, user_tester: str):
12
+ try:
13
+ self.tester = Catador.objects.get(usuarioCatador=user_tester)
14
+ self.session = SesionSensorial.objects.get(
15
+ codigo_sesion=code_session)
16
+ except (Catador.DoesNotExist, SesionSensorial.DoesNotExist):
17
+ return controller_error("Parámetros inexistentes")
18
+
19
+ def assignOrder(self):
20
+ self.checkAssignOrder()
21
+ if isinstance(self.order, Orden):
22
+ return self.order
23
+
24
+ with transaction.atomic():
25
+ orders_without_tester = Orden.objects.select_for_update().filter(
26
+ id_tecnica=self.session.tecnica, id_catador=None)
27
+
28
+ if not len(orders_without_tester):
29
+ return controller_error("Las ordenes se han acabado")
30
+
31
+ shuffle_orders = shuffleArray(orders_without_tester)
32
+ self.order_to_assign = shuffle_orders.pop()
33
+
34
+ self.order_to_assign.id_catador = self.tester
35
+ self.order_to_assign.save()
36
+
37
+ return self.order_to_assign
38
+
39
+ def checkAssignOrder(self):
40
+ if not self.tester or self.session:
41
+ return controller_error("Atributos no establecidos")
42
+
43
+ try:
44
+ res_order = Orden.objects.get(
45
+ id_tecnica=self.session.tecnica, id_catador=self.tester.id)
46
+ self.order = res_order
47
+ except Orden.DoesNotExist:
48
+ return controller_error("Catador sin orden")
tecnicas/controllers/presentador_controller.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..models import Presentador
2
+ from ..utils import controller_error
3
+
4
+
5
+ class PresentadorController():
6
+ def __init__(self):
7
+ pass
8
+
9
+ @staticmethod
10
+ def getPresenterByUsername(username: str):
11
+ try:
12
+ presenter = Presentador.objects.get(nombre_usuario=username)
13
+ except Presentador.DoesNotExist:
14
+ return controller_error("Presentador inexistente")
tecnicas/controllers/sesion_controller.py CHANGED
@@ -54,7 +54,8 @@ class SesionController():
54
  queryset = SesionSensorial.objects.select_related(
55
  "tecnica",
56
  "tecnica__tipo_tecnica",
57
- "tecnica__id_estilo").only(
 
58
  "codigo_sesion",
59
  "nombre_sesion",
60
  "fechaCreacion",
@@ -64,21 +65,41 @@ class SesionController():
64
 
65
  paginator = Paginator(queryset, elements_by_page)
66
  sessions_in_page = paginator.get_page(page)
 
 
67
  except PageNotAnInteger:
68
  return controller_error("indice invalido")
69
  except EmptyPage:
70
  return controller_error("sin registros de sessiones")
71
 
72
- return sessions_in_page
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
  @staticmethod
75
  def getNumberSessionsByCreator(user_name: str):
76
  try:
77
  creator = Presentador.objects.get(nombre_usuario=user_name)
78
- except Presentador.DoesNotExist:
79
- return controller_error("presentador invalido")
80
 
81
- number_sessions = SesionSensorial.objects.filter(
82
- creadoPor=creator).count()
83
 
84
- return number_sessions/9
 
 
 
54
  queryset = SesionSensorial.objects.select_related(
55
  "tecnica",
56
  "tecnica__tipo_tecnica",
57
+ "tecnica__id_estilo"
58
+ ).only(
59
  "codigo_sesion",
60
  "nombre_sesion",
61
  "fechaCreacion",
 
65
 
66
  paginator = Paginator(queryset, elements_by_page)
67
  sessions_in_page = paginator.get_page(page)
68
+
69
+ return sessions_in_page
70
  except PageNotAnInteger:
71
  return controller_error("indice invalido")
72
  except EmptyPage:
73
  return controller_error("sin registros de sessiones")
74
 
75
+ @staticmethod
76
+ def getSessionByCodePanelTester(code: str):
77
+ try:
78
+ session = SesionSensorial.objects.select_related(
79
+ "tecnica",
80
+ "tecnica__tipo_tecnica",
81
+ "tecnica__id_estilo"
82
+ ).only(
83
+ "codigo_sesion",
84
+ "nombre_sesion",
85
+ "tecnica__repecion",
86
+ "tecnica__instrucciones",
87
+ "tecnica__tipo_tecnica__nombre_tecnica",
88
+ "tecnica__id_estilo__nombre_estilo"
89
+ ).get(codigo_sesion=code)
90
+
91
+ return session
92
+ except SesionSensorial.DoesNotExist:
93
+ return controller_error("La sesión ya no existe")
94
 
95
  @staticmethod
96
  def getNumberSessionsByCreator(user_name: str):
97
  try:
98
  creator = Presentador.objects.get(nombre_usuario=user_name)
 
 
99
 
100
+ number_sessions = SesionSensorial.objects.filter(
101
+ creadoPor=creator).count()
102
 
103
+ return number_sessions/9
104
+ except Presentador.DoesNotExist:
105
+ return controller_error("presentador invalido")
tecnicas/controllers/tecnica_controller.py CHANGED
@@ -35,6 +35,14 @@ class TecnicaController():
35
  def deleteTechnique(self):
36
  self.technique.delete()
37
 
 
 
 
 
 
 
 
 
38
  @staticmethod
39
  def getTypesTechnique():
40
  showTecnicas = {}
 
35
  def deleteTechnique(self):
36
  self.technique.delete()
37
 
38
+ @staticmethod
39
+ def getTechniqueById(id: int):
40
+ try:
41
+ technique = Tecnica.objects.get(id)
42
+ return technique
43
+ except Tecnica.DoesNotExist:
44
+ return controller_error("Técnica no encontrada")
45
+
46
  @staticmethod
47
  def getTypesTechnique():
48
  showTecnicas = {}
tecnicas/middlewares/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .login_tester_middleware import LoginTesterMiddleware
tecnicas/middlewares/login_tester_middleware.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+
3
+
4
+ class LoginTesterMiddleware():
5
+ def __init__(self, get_response):
6
+ self.get_response = get_response
7
+
8
+ def __call__(self, req: HttpRequest):
9
+ url_protected = "/cata/testers/"
10
+
11
+ if req.path.startswith(url_protected):
12
+ if not "cata_username" in req.session:
13
+ from django.shortcuts import redirect
14
+ from django.urls import reverse
15
+ return redirect(reverse("cata_system:catador_login"))
16
+
17
+ response = self.get_response(req)
18
+
19
+ return response
tecnicas/migrations/0015_alter_sesionsensorial_codigo_sesion.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-09-18 01:05
2
+
3
+ import shortuuid.main
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('tecnicas', '0014_alter_sesionsensorial_codigo_sesion_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/0016_alter_sesionsensorial_codigo_sesion_participacion.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-09-18 19:49
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', '0015_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='Participacion',
22
+ fields=[
23
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24
+ ('activo', models.BooleanField(default=False)),
25
+ ('finalizado', models.BooleanField(default=True)),
26
+ ('catador', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='catador_participacion', to='tecnicas.catador')),
27
+ ('tecnica', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tecnica_participacion', to='tecnicas.tecnica')),
28
+ ],
29
+ ),
30
+ ]
tecnicas/migrations/0017_alter_sesionsensorial_codigo_sesion_and_more.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-09-18 19:57
2
+
3
+ import shortuuid.main
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('tecnicas', '0016_alter_sesionsensorial_codigo_sesion_participacion'),
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
+ migrations.DeleteModel(
20
+ name='Participacion',
21
+ ),
22
+ ]
tecnicas/migrations/0018_alter_sesionsensorial_codigo_sesion_participacion.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-09-18 19:58
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', '0017_alter_sesionsensorial_codigo_sesion_and_more'),
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='Participacion',
22
+ fields=[
23
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24
+ ('activo', models.BooleanField(default=False)),
25
+ ('finalizado', models.BooleanField(default=True)),
26
+ ('catador', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='catador_participacion', to='tecnicas.catador')),
27
+ ('tecnica', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tecnica_participacion', to='tecnicas.tecnica')),
28
+ ],
29
+ ),
30
+ ]
tecnicas/models/__init__.py CHANGED
@@ -25,4 +25,6 @@ from .dato_valor import ValorDecimal
25
  from .dato_valor import ValorBooleano
26
 
27
  from .orden import Orden
28
- from .orden import Posicion
 
 
 
25
  from .dato_valor import ValorBooleano
26
 
27
  from .orden import Orden
28
+ from .orden import Posicion
29
+
30
+ from .participacion import Participacion
tecnicas/models/participacion.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.db import models
2
+ from .tecnica import Tecnica
3
+ from .catador import Catador
4
+
5
+
6
+ class Participacion(models.Model):
7
+ tecnica = models.ForeignKey(
8
+ Tecnica, on_delete=models.CASCADE, related_name="tecnica_participacion")
9
+ catador = models.ForeignKey(
10
+ Catador, on_delete=models.CASCADE, related_name="catador_participacion")
11
+ activo = models.BooleanField(default=False)
12
+ finalizado = models.BooleanField(default=True)
13
+
14
+ def __str__(self):
15
+ return f"{self.catador.usuarioCatador} {'activo' if self.activo else 'no activo'}"
tecnicas/models/sesion_sensorial.py CHANGED
@@ -17,4 +17,4 @@ class SesionSensorial(models.Model):
17
  Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
18
 
19
  def __str__(self):
20
- return self.codigo_sesion
 
17
  Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
18
 
19
  def __str__(self):
20
+ return self.nombre_sesion if self.nombre_sesion else self.codigo_sesion
tecnicas/pato.py DELETED
@@ -1,42 +0,0 @@
1
- from django.db import models
2
-
3
- class Presentador(models.Model):
4
- nombre = models.CharField(max_length=255)
5
- apellido = models.CharField(max_length=255)
6
- nombre_usuario = models.CharField(max_length=255)
7
- contrasena = models.CharField(max_length=255)
8
-
9
- class Palabra(models.Model):
10
- nombre_palabra = models.CharField(max_length=255, unique=True)
11
-
12
- class Vocabualario(models.Model):
13
- nomre_vocabulario = models.CharField(max_length=255, unique=True)
14
- palabras = models.ManyToManyField(Palabra)
15
-
16
- class EstiloPalabra(models.Model):
17
- nombre_estilo = models.CharField(max_length=255, unique=True)
18
-
19
- class EsAtributo(models.Model):
20
- id_estilo = models.ForeignKey(EstiloPalabra, on_delete=models.CASCADE, related_name="estilo_esatributo")
21
-
22
- class ListaPalabra(models.Model):
23
- id_palabra = models.ForeignKey(Palabra, on_delete=models.CASCADE, related_name="palabra_listapalabras")
24
- id_atributos = models.ForeignKey(EsAtributo, on_delete=models.CASCADE, related_name="atributo_listapalabras")
25
-
26
- class EsVocabulario(models.Model):
27
- id_estilo = models.ForeignKey(EstiloPalabra, on_delete=models.CASCADE, related_name="estilo_esvacabulario")
28
- id_vocabulario = models.ForeignKey(Vocabualario, on_delete=models.CASCADE, related_name="vocabulario_esvocabulario")
29
-
30
- class Tecnica(models.Model):
31
- nombre_tecnica = models.CharField(max_length=255)
32
- maximas_repeticiones = models.IntegerField(default=0)
33
- repecion = models.IntegerField(default=0)
34
- limite_catadores = models.IntegerField()
35
- instrucciones = models.CharField(max_length=255)
36
- id_estilo = models.ForeignKey(EstiloPalabra, on_delete=models.CASCADE, related_name="estilo_tecnica")
37
-
38
- class SesionSensorial(models.Model):
39
- fechaCreacion = models.DateTimeField("date published")
40
- activo = models.BooleanField(default=False)
41
- creadoPor = models.ForeignKey(Presentador, on_delete=models.CASCADE, related_name="presentador_sesion")
42
- tecnica = models.ForeignKey(Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tecnicas/static/img/check.svg ADDED
tecnicas/static/img/exit.svg ADDED
tecnicas/static/img/monitor.svg ADDED
tecnicas/static/js/details-session.js CHANGED
@@ -16,7 +16,7 @@ function startRepetition() {
16
  const inputUser = document.createElement("input");
17
  inputUser.type = "hidden";
18
  inputUser.name = "username";
19
- inputUser.value = "pollo";
20
 
21
  actionForm.appendChild(inputAction);
22
  actionForm.appendChild(inputUser);
 
16
  const inputUser = document.createElement("input");
17
  inputUser.type = "hidden";
18
  inputUser.name = "username";
19
+ inputUser.value = "aguBido";
20
 
21
  actionForm.appendChild(inputAction);
22
  actionForm.appendChild(inputUser);
tecnicas/static/js/start-tester-test.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ function startTest() {
2
+ const actionForms = document.querySelector(".ct-action-form");
3
+ actionForms.submit();
4
+ }
tecnicas/templates/tecnicas/cata-login.html CHANGED
@@ -4,20 +4,32 @@
4
 
5
  {% block content %}
6
  <article class="w-full h-full flex flex-col justify-center items-center bg-gray-600">
7
- <form action="./auth" method="post" class="bg-gray-200 w-xl p-8 rounded-xl">
 
8
  <header class="text-center">
9
  <h1 class="text-5xl font-bold">Cateo System</h1>
10
  <p class="text-2xl font-medium mt-3">Catadores</p>
11
  </header>
12
- <section class="flex flex-col gap-6 items-center w-full mt-5">
 
 
 
 
 
 
 
 
 
13
  <label for="id">
14
- <input type="text" name="id" id="id" placeholder="Codigo de sesion"
15
- class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700" required>
 
16
  </label>
17
 
18
  <label for="id">
19
- <input type="text" name="id" id="id" placeholder="Nombre de usuario"
20
- class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700" required>
 
21
  </label>
22
 
23
  <section class="flex flex-row flex-wrap gap-4 w-f ull justify-center">
@@ -25,7 +37,16 @@
25
  class="text-white bg-blue-600 hover:bg-blue-700 active:outline-none active:ring-4 active:ring-blue-300 font-medium rounded-xl text-xl px-8 py-2 text-center uppercase">Ingresar</button>
26
  </section>
27
 
28
- </section>
29
  </form>
30
  </article>
 
 
 
 
 
 
 
 
 
31
  {% endblock %}
 
4
 
5
  {% block content %}
6
  <article class="w-full h-full flex flex-col justify-center items-center bg-gray-600">
7
+ <form action="" method="post" class="bg-gray-200 w-xl p-8 rounded-xl">
8
+ {% csrf_token %}
9
  <header class="text-center">
10
  <h1 class="text-5xl font-bold">Cateo System</h1>
11
  <p class="text-2xl font-medium mt-3">Catadores</p>
12
  </header>
13
+
14
+ {% if error %}
15
+ <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
16
+ <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
17
+ {{ error }}
18
+ </p>
19
+ </article>
20
+ {% endif %}
21
+
22
+ <article class="flex flex-col gap-6 items-center w-full mt-5">
23
  <label for="id">
24
+ <input type="text" name="code_session" id="id" placeholder="Codigo de sesion"
25
+ class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700"
26
+ required>
27
  </label>
28
 
29
  <label for="id">
30
+ <input type="text" name="user_tester" id="id" placeholder="Nombre de usuario"
31
+ class="placeholder:text-gray-100 placeholder:text-xl bg-gray-400 py-3 px-6 rounded-xl w-sm border-b-2 border-blue-700"
32
+ required>
33
  </label>
34
 
35
  <section class="flex flex-row flex-wrap gap-4 w-f ull justify-center">
 
37
  class="text-white bg-blue-600 hover:bg-blue-700 active:outline-none active:ring-4 active:ring-blue-300 font-medium rounded-xl text-xl px-8 py-2 text-center uppercase">Ingresar</button>
38
  </section>
39
 
40
+ </article>
41
  </form>
42
  </article>
43
+ {% endblock %}
44
+
45
+ {% block extra_js %}
46
+ <script>
47
+ const error = document.querySelector(".ct-notification-error")
48
+ setTimeout(() => {
49
+ error.classList.add("hidden")
50
+ }, 4000)
51
+ </script>
52
  {% endblock %}
tecnicas/templates/tecnicas/forms_tester/main_tester.html ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'tecnicas/layouts/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title %}Detalles Sesion{% endblock %}
5
+
6
+ {% block content %}
7
+ <article class="w-full flex flex-col justify-center items-center bg-gray-600 mt-10 mb-10">
8
+ <article class="flex flex-col gap-8 bg-gray-400 p-10 rounded-2xl">
9
+ <header class="text-center flex-row w-full flex justify-around items-center flex-wrap gap-10">
10
+ <h1 class="text-white rounded-xl font-bold text-2xl bg-gray-600 p-4 flex-1">
11
+ Panel principal de Catadores
12
+ </h1>
13
+ </header>
14
+
15
+ <article>
16
+ <p class="text-2xl font-medium text-center bg-gray-200 p-4 rounded-lg">
17
+ Información sobre la sesión en la que participa
18
+ </p>
19
+ </article>
20
+
21
+ {% if error %}
22
+ <hr>
23
+
24
+ <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
25
+ <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
26
+ {{ error }}
27
+ </p>
28
+ </article>
29
+ {% endif %}
30
+
31
+ <hr>
32
+
33
+ <article
34
+ class="text-white rounded-xl grid grid-cols-1 gap-3 text-center *:bg-gray-500 *:flex *:flex-wrap *:items-center *:justify-center *:gap-x-2 *:p-4 *:rounded-2xl">
35
+ {% if session %}
36
+ <section>
37
+ <p class="text-xl font-bold">
38
+ Código:
39
+ </p>
40
+ <p class="font-sans text-xl font-normal">
41
+ {{ session.codigo_sesion }}
42
+ </p>
43
+ </section>
44
+
45
+ <section>
46
+ <p class="text-xl font-bold">
47
+ {% if session.nombre_sesion %}
48
+ {{ session.nombre_sesion }}
49
+ {% else %}
50
+ Sin nombre
51
+ {% endif %}
52
+ </p>
53
+ </section>
54
+
55
+ <section>
56
+ <p class="text-xl font-medium">
57
+ Esta sesión usa la técnica <span class="uppercase">{{ session.tecnica.tipo_tecnica }}</span>
58
+ </p>
59
+ </section>
60
+
61
+ <section>
62
+ <p class="text-xl font-medium">
63
+ Esta sesión usa el estilo de palabras
64
+ “<span class="uppercase">{{ session.tecnica.id_estilo }}</span>”
65
+ </p>
66
+ </section>
67
+
68
+ <section>
69
+ <p class="text-xl font-medium">
70
+ Repetición número {{ session.tecnica.repecion }}
71
+ </p>
72
+ </section>
73
+
74
+ <section>
75
+ <p class="text-xl">
76
+ {{ session.tecnica.instrucciones }}
77
+ </p>
78
+ </section>
79
+ {% else %}
80
+ <section>
81
+ <p class="text-xl font-medium">
82
+ Ha ocurrido un error en renderizar esta vista
83
+ </p>
84
+ </section>
85
+ {% endif %}
86
+ </article>
87
+
88
+ <hr>
89
+
90
+ <article class="flex flex-wrap gap-10">
91
+ <button
92
+ class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-green-500 border-green-800 transition-all rounded-xl bg-green-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2"
93
+ onclick="startTest()">
94
+ Iniciar técnica
95
+ <figure class="w-10">
96
+ <img src="{% static 'img/check.svg' %}" alt="flechas girando" class="invert">
97
+ </figure>
98
+ </button>
99
+ <button
100
+ class="flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-red-500 border-red-800 transition-all rounded-xl bg-red-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2">
101
+ Salir se la sesión
102
+ <figure class="w-10">
103
+ <img src="{% static 'img/exit.svg' %}" alt="bote de basura" class="invert">
104
+ </figure>
105
+ </button>
106
+ </article>
107
+
108
+ <form action="" method="post" class="hidden ct-action-form">
109
+ {% csrf_token %}
110
+ <input type="hidden" value="pipo" name="action">
111
+ </form>
112
+ </article>
113
+ </article>
114
+ {% endblock %}
115
+
116
+ {% block extra_js %}
117
+ <script src="{% static 'js/start-tester-test.js' %}"></script>
118
+ {% endblock %}
tecnicas/templates/tecnicas/manage_sesions/detalles-sesion.html CHANGED
@@ -26,6 +26,14 @@
26
  </article>
27
  {% endif %}
28
 
 
 
 
 
 
 
 
 
29
  <p class="font-bold text-2xl border-b-2">
30
  Información general
31
  </p>
@@ -149,6 +157,14 @@
149
  <img src="{% static 'img/giro.svg' %}" alt="flechas girando" class="invert">
150
  </figure>
151
  </button>
 
 
 
 
 
 
 
 
152
  {% endif %}
153
 
154
  <button
 
26
  </article>
27
  {% endif %}
28
 
29
+ {% if message %}
30
+ <article class="bg-green-600 p-4 text-white rounded-xl ct-notification-error">
31
+ <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
32
+ {{ message }}
33
+ </p>
34
+ </article>
35
+ {% endif %}
36
+
37
  <p class="font-bold text-2xl border-b-2">
38
  Información general
39
  </p>
 
157
  <img src="{% static 'img/giro.svg' %}" alt="flechas girando" class="invert">
158
  </figure>
159
  </button>
160
+ {% else %}
161
+ <button
162
+ class="ct-btn-start-repition flex-1 uppercase text-lg tracking-wider p-4 border-b-2 active:border-b-0 active:border-t-2 active:border-orange-500 border-orange-800 transition-all rounded-xl bg-orange-600 text-white font-bold disabled:bg-amber-600 flex flex-col justify-center items-center gap-2">
163
+ Monitorear repetición
164
+ <figure class="w-10">
165
+ <img src="{% static 'img/monitor.svg' %}" alt="flechas girando" class="invert">
166
+ </figure>
167
+ </button>
168
  {% endif %}
169
 
170
  <button
tecnicas/urls.py CHANGED
@@ -12,10 +12,6 @@ urlpatterns = [
12
  views.autentication,
13
  name="autenticacion"),
14
 
15
- path("catador-login",
16
- views.catadorLogin,
17
- name="catador_login"),
18
-
19
 
20
  # Gestion de catadores
21
  path("panel-catadores",
@@ -67,6 +63,15 @@ urlpatterns = [
67
  name="detalles_sesion"),
68
 
69
 
 
 
 
 
 
 
 
 
 
70
  # APIs
71
  path("nueva-etiqueta",
72
  views.newTag,
 
12
  views.autentication,
13
  name="autenticacion"),
14
 
 
 
 
 
15
 
16
  # Gestion de catadores
17
  path("panel-catadores",
 
63
  name="detalles_sesion"),
64
 
65
 
66
+ # Vistas para catadores
67
+ path("catador-login",
68
+ views.testerLogin,
69
+ name="catador_login"),
70
+
71
+ path("testers/catador-main",
72
+ views.mainTesterForm,
73
+ name="catador_main"),
74
+
75
  # APIs
76
  path("nueva-etiqueta",
77
  views.newTag,
tecnicas/utils/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
  from .code_generate import generarCodigo
2
  from .code_generate import generarCodigos
3
  from .personal_errors import general_error, controller_error
 
 
1
  from .code_generate import generarCodigo
2
  from .code_generate import generarCodigos
3
  from .personal_errors import general_error, controller_error
4
+ from .shuffle_arrays import shuffleArray
tecnicas/utils/shuffle_arrays.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import random
2
+
3
+
4
+ def shuffleArray(obj_list: list):
5
+ new_obj_list = obj_list[:]
6
+ random.shuffle(new_obj_list)
7
+ return new_obj_list
tecnicas/views/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from .autentication import autentication
2
- from .login_catador import catadorLogin
3
  from .main_panel import mainPanel
4
 
5
  from .sessions_management.sessions_panel import sesionsPanel
@@ -18,3 +18,5 @@ from .tester_management.tester_search import testerSearch
18
 
19
  from .apis.api_tag import newTag
20
  from .apis.api_words import words
 
 
 
1
  from .autentication import autentication
2
+ from .login_tester import testerLogin
3
  from .main_panel import mainPanel
4
 
5
  from .sessions_management.sessions_panel import sesionsPanel
 
18
 
19
  from .apis.api_tag import newTag
20
  from .apis.api_words import words
21
+
22
+ from .tester_forms.main_tester_form import mainTesterForm
tecnicas/views/login_catador.py DELETED
@@ -1,4 +0,0 @@
1
- from django.shortcuts import render
2
-
3
- def catadorLogin(req):
4
- return render(req, "tecnicas/cata-login.html")
 
 
 
 
 
tecnicas/views/login_tester.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from ..utils import general_error
5
+ from ..controllers import LoginTesterController
6
+
7
+
8
+ def testerLogin(req: HttpRequest):
9
+ if req.method == "GET":
10
+ return render(req, "tecnicas/cata-login.html")
11
+ elif req.method == "POST":
12
+ tester_user = req.POST.get("user_tester")
13
+ session_code = req.POST.get("code_session")
14
+ if not tester_user or not session_code:
15
+ return general_error("Se esperan credenciales")
16
+
17
+ login_controller = LoginTesterController()
18
+
19
+ existCredentials = login_controller.existCredential(
20
+ tester_user, session_code)
21
+ if isinstance(existCredentials, dict):
22
+ context = {"error": existCredentials["error"]}
23
+ return render(req, "tecnicas/cata-login.html", context)
24
+
25
+ taster_participation = login_controller.validateEntry()
26
+ if isinstance(taster_participation, dict):
27
+ context = {"error": taster_participation["error"]}
28
+ return render(req, "tecnicas/cata-login.html", context)
29
+
30
+ req.session["cata_username"] = tester_user
31
+ req.session["code_session"] = session_code
32
+
33
+ req.session.set_expiry(20*60)
34
+ return redirect(reverse("cata_system:catador_main"))
35
+ else:
36
+ return render(req, "tecnicas/cata-login.html")
tecnicas/views/sessions_management/session_details.py CHANGED
@@ -12,12 +12,12 @@ def sessionDetails(req: HttpRequest, session_code: str):
12
  elif req.method == "POST":
13
  if req.POST["action"] == "start_session":
14
  response = DetallesSesionController.startRepetition(
15
- session_code, req.POST.get("username"))
16
  if isinstance(response, dict):
17
  context["error"] = response["error"]
18
  return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
19
-
20
- return redirect(reverse("cata_system:----"))
21
  elif req.POST.get("action") == "delete_session":
22
  pass
23
  elif req.POST.get("action") == "monitor_session":
 
12
  elif req.method == "POST":
13
  if req.POST["action"] == "start_session":
14
  response = DetallesSesionController.startRepetition(
15
+ session_code=session_code, username=req.POST["username"])
16
  if isinstance(response, dict):
17
  context["error"] = response["error"]
18
  return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
19
+ context["message"] = "La sesión ha iniciado"
20
+ return render(req, "tecnicas/manage_sesions/detalles-sesion.html", context)
21
  elif req.POST.get("action") == "delete_session":
22
  pass
23
  elif req.POST.get("action") == "monitor_session":
tecnicas/views/tester_forms/main_tester_form.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest, JsonResponse
2
+ from django.shortcuts import render
3
+ from ...controllers import SesionController, MainTesterFormController
4
+
5
+
6
+ def mainTesterForm(req: HttpRequest):
7
+ session = SesionController.getSessionByCodePanelTester(
8
+ req.session["code_session"])
9
+
10
+ context = {
11
+ "session": session
12
+ }
13
+
14
+ if req.method == "GET":
15
+ return render(req, "tecnicas/forms_tester/main_tester.html", context)
16
+ elif req.method == "POST":
17
+ if req.POST["action"] == "start_posting":
18
+ view_controller = MainTesterFormController(
19
+ req.session["code_session"], req.session["cata_username"])
20
+
21
+ order = view_controller.assignOrder()
22
+ if isinstance(order, dict):
23
+ context["error"] = order["error"]
24
+ return render(req, "tecnicas/forms_tester/main_tester.html", context)
25
+
26
+ return render(req, "tecnicas/forms_tester/main_tester.html", context)
27
+ elif req.POST["action"] == "close_session":
28
+ pass
29
+ else:
30
+ context["error"] = "Acción sin especificar"
31
+ return render(req, "tecnicas/forms_tester/main_tester.html", context)
32
+ else:
33
+ return JsonResponse({"error": "metodo no permitido"})