Norberto Montalvo García commited on
Commit
04c0edb
·
unverified ·
2 Parent(s): 573ffa8 b0a47bb

Merge pull request #1 from CascoArcilla/HU3

Browse files

Creación de Sesion Sensorial aplicando escalas simples.

Files changed (42) hide show
  1. .gitignore +1 -0
  2. cata_system/urls.py +2 -0
  3. cata_system/views.py +5 -0
  4. tecnicas/admin.py +11 -1
  5. tecnicas/controllers/__init__.py +7 -1
  6. tecnicas/controllers/escala_controller.py +55 -50
  7. tecnicas/controllers/estilo_palabras_controller.py +32 -0
  8. tecnicas/controllers/ordenes_controller.py +76 -0
  9. tecnicas/controllers/palabras_controller.py +25 -0
  10. tecnicas/controllers/productos_controller.py +39 -0
  11. tecnicas/controllers/sesion_controller.py +41 -0
  12. tecnicas/controllers/tecnica_controller.py +27 -3
  13. tecnicas/forms/__init__.py +2 -1
  14. tecnicas/forms/word_form.py +36 -0
  15. tecnicas/migrations/0012_alter_sesionsensorial_codigo_sesion_and_more.py +25 -0
  16. tecnicas/migrations/0013_alter_sesionsensorial_codigo_sesion_and_more.py +24 -0
  17. tecnicas/migrations/0014_alter_sesionsensorial_codigo_sesion_and_more.py +25 -0
  18. tecnicas/models/escala.py +1 -1
  19. tecnicas/models/etiquetas_escala.py +10 -5
  20. tecnicas/models/orden.py +7 -1
  21. tecnicas/models/sesion_sensorial.py +11 -7
  22. tecnicas/models/tecnica.py +16 -3
  23. tecnicas/static/img/list.svg +7 -0
  24. tecnicas/static/img/lupa.svg +7 -0
  25. tecnicas/static/js/create-session.js +141 -0
  26. tecnicas/static/js/panel-codes.js +14 -1
  27. tecnicas/static/js/panel-words.js +256 -52
  28. tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html +1 -1
  29. tecnicas/templates/tecnicas/create_sesion/configuracion-panel-words.html +81 -30
  30. tecnicas/templates/tecnicas/create_sesion/creando_sesion.html +31 -0
  31. tecnicas/test/test_controller_words.py +27 -0
  32. tecnicas/test/test_words.py +52 -20
  33. tecnicas/urls.py +1 -0
  34. tecnicas/utils/__init__.py +1 -1
  35. tecnicas/utils/{general_error.py → personal_errors.py} +8 -2
  36. tecnicas/views/__init__.py +2 -1
  37. tecnicas/views/api_words.py +31 -21
  38. tecnicas/views/configuration_panel_basic.py +15 -3
  39. tecnicas/views/configuration_panel_codes.py +17 -17
  40. tecnicas/views/configuration_panel_tags.py +9 -6
  41. tecnicas/views/configuration_panel_words.py +39 -6
  42. tecnicas/views/create_session.py +163 -0
.gitignore CHANGED
@@ -167,6 +167,7 @@ cython_debug/
167
 
168
  # Others files
169
  some_test
 
170
 
171
  # PyCharm
172
  # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
 
167
 
168
  # Others files
169
  some_test
170
+ others_files
171
 
172
  # PyCharm
173
  # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
cata_system/urls.py CHANGED
@@ -16,8 +16,10 @@ Including another URLconf
16
  """
17
  from django.contrib import admin
18
  from django.urls import path, include
 
19
 
20
  urlpatterns = [
 
21
  path('cata/', include("tecnicas.urls")),
22
  path('admin/', admin.site.urls),
23
 
 
16
  """
17
  from django.contrib import admin
18
  from django.urls import path, include
19
+ from cata_system import views
20
 
21
  urlpatterns = [
22
+ path("", views.home, name="home"),
23
  path('cata/', include("tecnicas.urls")),
24
  path('admin/', admin.site.urls),
25
 
cata_system/views.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from django.http import HttpRequest
2
+ from django.shortcuts import redirect
3
+
4
+ def home(req: HttpRequest):
5
+ return redirect('/cata/')
tecnicas/admin.py CHANGED
@@ -16,6 +16,11 @@ from .models import Palabra
16
 
17
  from .models import Etiqueta
18
 
 
 
 
 
 
19
  # Register your models here.
20
  admin.site.register(CategoriaTecnica)
21
  admin.site.register(TipoEscala)
@@ -30,4 +35,9 @@ admin.site.register(Tecnica)
30
  admin.site.register(SesionSensorial)
31
 
32
  admin.site.register(EsAtributo)
33
- admin.site.register(Palabra)
 
 
 
 
 
 
16
 
17
  from .models import Etiqueta
18
 
19
+ 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)
26
  admin.site.register(TipoEscala)
 
35
  admin.site.register(SesionSensorial)
36
 
37
  admin.site.register(EsAtributo)
38
+ admin.site.register(Palabra)
39
+
40
+ admin.site.register(Escala)
41
+ admin.site.register(EtiquetasEscala)
42
+
43
+ admin.site.register(Producto)
tecnicas/controllers/__init__.py CHANGED
@@ -1,2 +1,8 @@
1
  from .tecnica_controller import TecnicaController
2
- from .escala_controller import EscalaController
 
 
 
 
 
 
 
1
  from .tecnica_controller import TecnicaController
2
+ from .escala_controller import EscalaController
3
+ from .productos_controller import ProductosController
4
+ from .ordenes_controller import OrdenesController
5
+ from .palabras_controller import PalabrasController
6
+ from .estilo_palabras_controller import EstiloPalabrasController
7
+ from .palabras_controller import PalabrasController
8
+ from .sesion_controller import SesionController
tecnicas/controllers/escala_controller.py CHANGED
@@ -1,65 +1,70 @@
1
- from ..models import TipoEscala, Etiqueta, EtiquetasEscala, Escala, Tecnica
 
 
2
 
3
 
4
  class EscalaController():
5
-
6
  scale: Escala
 
7
 
8
- def __init__(self, scale):
9
- self.scale = scale or None
 
 
 
 
10
 
11
- def setAndSaveScale(self, type_scale: TipoEscala, size: int, technique: Tecnica):
12
- self.scale = Escala.objects.create(
13
- id_tipo_escala=type_scale,
14
- longitud=size,
15
- tecnica=technique
16
  )
17
 
18
- def setScale(self, scale):
19
- self.scale = scale
 
 
 
 
20
 
21
- def realte_tags_type_cotinue(self, tags: list):
22
- tag = Etiqueta.objects.get(id=tags["punto_inicial"])
23
- start_point = EtiquetasEscala.objects.create(
24
- id_escala=self.scale.id,
25
- id_etiqueta=tag,
26
- posicion=1
27
- )
28
 
29
- tag = Etiqueta.objects.get(id=tags["punto_medio"])
30
- half_point = EtiquetasEscala.objects.create(
31
- id_escala=self.scale.id,
32
- id_etiqueta=tag,
33
- posicion=2
34
- )
 
35
 
36
- tag = Etiqueta.objects.get(id=tags["punto_final"])
37
- end_point = EtiquetasEscala.objects.create(
38
- id_escala=self.scale.id,
39
- id_etiqueta=tag,
40
- posicion=3
41
- )
42
 
43
- self.tags = [
44
- ("start", start_point),
45
- ("medium", half_point),
46
- ("end", end_point)
47
- ]
48
 
49
- return self.tags
 
 
 
50
 
51
- def realte_tags_type_structure(self, tags: dict):
52
- index = 1
53
- self.tags = []
 
 
 
 
 
 
54
 
55
- for name, id_tag in tags.items():
56
- tag = Etiqueta.objects.get(id=id_tag)
57
- related_tag = EtiquetasEscala(
58
- id_escala=self.scale.id,
59
- id_etiqueta=tag,
60
- posicion=index
61
- )
62
- self.tags.append((name, related_tag))
63
- index += 1
64
-
65
- return self.tags
 
1
+ from ..models import Etiqueta, EtiquetasEscala, Escala, TipoEscala
2
+ from django.db import DatabaseError
3
+ from ..utils import controller_error
4
 
5
 
6
  class EscalaController():
 
7
  scale: Escala
8
+ tags_relation: dict[str, EtiquetasEscala]
9
 
10
+ def __init__(self, data):
11
+ self.scale = Escala(
12
+ id_tipo_escala=TipoEscala.objects.get(id=data["id_scale"]),
13
+ longitud=data["size"],
14
+ tecnica=data["technique"]
15
+ )
16
 
17
+ def setScale(self, newData):
18
+ self.scale = Escala(
19
+ id_tipo_escala=TipoEscala.objects.get(id=newData["id_scale"]),
20
+ longitud=newData["size"],
21
+ tecnica=newData["technique"]
22
  )
23
 
24
+ def saveScale(self):
25
+ try:
26
+ self.scale.save()
27
+ return self.scale
28
+ except DatabaseError as error:
29
+ return controller_error("error al guardar la escala")
30
 
31
+ def deleteScale(self):
32
+ self.scale.delete()
 
 
 
 
 
33
 
34
+ def addAndSaveTags(self, tags: dict):
35
+ self.tags_relation = {}
36
+ if self.scale.id_tipo_escala.nombre_escala == "cotinua":
37
+ ok_tags = self.realte_tags_type_cotinue(tags)
38
+ if ok_tags["error"]:
39
+ return ok_tags
40
+ return self.tags_relation
41
 
42
+ elif self.scale.id_tipo_escala.nombre_escala == "estructurada":
43
+ ok_tags = self.realte_tags_type_structure(tags)
44
+ if ok_tags["error"]:
45
+ return ok_tags
46
+ return self.tags_relation
 
47
 
48
+ def deleteRelationshipsWithLabels(self):
49
+ for name, tag in self.tags_relation.items():
50
+ tag.delete()
 
 
51
 
52
+ def realteTags(self, tags: dict):
53
+ try:
54
+ index = 1
55
+ self.tags_relation = {}
56
 
57
+ for name, id_tag in tags.items():
58
+ tag = Etiqueta.objects.get(id=id_tag)
59
+ related_tag = EtiquetasEscala.objects.create(
60
+ id_escala=self.scale,
61
+ id_etiqueta=tag,
62
+ posicion=index
63
+ )
64
+ self.tags_relation[name] = related_tag
65
+ index += 1
66
 
67
+ return self.tags_relation
68
+ except DatabaseError as error:
69
+ self.deleteRelationshipsWithLabels()
70
+ return controller_error("error guardar relacion etiqueta escala")
 
 
 
 
 
 
 
tecnicas/controllers/estilo_palabras_controller.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..models import EsAtributo, Tecnica, Palabra
2
+ from django.db import DatabaseError
3
+ from ..utils import controller_error
4
+
5
+
6
+ class EstiloPalabrasController():
7
+ def __init__(self, technique: Tecnica = None, words: list[Palabra] = None):
8
+ self.technique = technique
9
+ self.list_words = words
10
+
11
+ def setTechnique(self, technique: Tecnica):
12
+ self.technique = technique
13
+
14
+ def setListWords(self, new_words: list[Palabra]):
15
+ self.list_words = new_words
16
+
17
+ def createAndSaveInstaceStyle(self):
18
+ try:
19
+ self.instanceStyle = EsAtributo.objects.create(
20
+ id_tecnica=self.technique)
21
+ return self.instanceStyle
22
+ except DatabaseError as error:
23
+ return controller_error("error al registrar el estilo con tecnica")
24
+
25
+ def relatedWords(self):
26
+ if not self.list_words:
27
+ return controller_error("no existen palabras para asociar")
28
+ try:
29
+ self.instanceStyle.palabras.add(*self.list_words)
30
+ return self.instanceStyle.palabras
31
+ except DatabaseError as error:
32
+ return controller_error("error al relacionar palabras con el estilo tecnica")
tecnicas/controllers/ordenes_controller.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..models import Orden, Posicion, Producto, Tecnica
2
+ from ..utils import controller_error
3
+ from django.db import DatabaseError
4
+
5
+
6
+ class OrdenesController():
7
+ products: list[Producto]
8
+ technique: Tecnica
9
+ raw_list_orders: list[dict]
10
+ orders: list[Orden]
11
+ positions: list[Posicion]
12
+
13
+ def __init__(self, raw_orders: list[dict], list_products: list[Producto], technique: Tecnica):
14
+ self.products = list_products
15
+ self.technique = technique
16
+ self.raw_list_orders = raw_orders
17
+
18
+ def serRawOrders(self, new_raw_orders: list[dict]):
19
+ self.raw_list_orders = new_raw_orders
20
+
21
+ def setOrdersToSave(self):
22
+ self.orders = []
23
+ for raw in self.raw_list_orders:
24
+ self.orders.append(Orden(id_tecnica=self.technique))
25
+
26
+ def saveOrders(self):
27
+ if not self.orders:
28
+ return controller_error("no se han establecido las ordenes para guardar")
29
+ try:
30
+ for order in self.orders:
31
+ order.save()
32
+ return self.orders
33
+ except DatabaseError as error:
34
+ return controller_error("error al guardar las ordenes")
35
+
36
+ def setPositions(self):
37
+ codes_ids_products = {}
38
+ for product in self.products:
39
+ codes_ids_products[product.codigoProducto] = product.id
40
+ codes_expect = list(codes_ids_products.keys())
41
+
42
+ if len(self.orders) != len(self.raw_list_orders):
43
+ return controller_error("el numero de ordenes guardados no coinciden con los recibidos")
44
+
45
+ self.positions = []
46
+ for index, order in enumerate(self.raw_list_orders):
47
+ received_codes_order = list(order.keys())
48
+
49
+ if set(received_codes_order) != set(codes_expect):
50
+ return controller_error("las ordenes mandadas no contienen los productos esperados")
51
+
52
+ for name, position_index in order.items():
53
+ list_product_use = [product for product in self.products
54
+ if product.codigoProducto == name]
55
+
56
+ if len(list_product_use) != 1:
57
+ return controller_error("no pueden existir dos productos que ocupen la misma posicion de un orden")
58
+
59
+ product_use = list_product_use[0]
60
+ new_position = Posicion(
61
+ id_producto=product_use,
62
+ id_orden=self.orders[index],
63
+ posicion=position_index
64
+ )
65
+ self.positions.append(new_position)
66
+ return self.positions
67
+
68
+ def savePositions(self):
69
+ if not self.positions:
70
+ return controller_error("no se han establecido posiciones para guargar")
71
+ try:
72
+ for position in self.positions:
73
+ position.save()
74
+ return self.positions
75
+ except DatabaseError as error:
76
+ return controller_error("error al guardar las posiciones")
tecnicas/controllers/palabras_controller.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ..models import Palabra
2
+ from django.db import DatabaseError
3
+ from ..utils import controller_error
4
+
5
+
6
+ class PalabrasController():
7
+ ids_words: list[int]
8
+ words: list[Palabra]
9
+
10
+ def __init__(self, ids: list[int]):
11
+ self.ids_words = ids
12
+
13
+ def setIdsWords(self, new_ids: list[int]):
14
+ self.ids_words = new_ids
15
+
16
+ def setWords(self):
17
+ self.words = []
18
+ try:
19
+ searched_words = Palabra.objects.filter(id__in=self.ids_words)
20
+ if not len(searched_words):
21
+ return controller_error("no se han encontrado registros")
22
+ self.words = searched_words
23
+ return self.words
24
+ except DatabaseError as error:
25
+ return controller_error("error al guardar buscar palabras")
tecnicas/controllers/productos_controller.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.db import DatabaseError
2
+ from ..utils import controller_error
3
+ from ..models import Producto, Tecnica
4
+
5
+
6
+ class ProductosController():
7
+ list_codes: list[str]
8
+ list_product: list[Producto]
9
+ technique: Tecnica
10
+
11
+ def __init__(self, codes: list[str], technique: Tecnica):
12
+ self.list_codes = codes
13
+ self.technique = technique
14
+
15
+ def setListCodes(self, new_codes: list[str]):
16
+ self.list_codes = new_codes
17
+
18
+ def setTechnique(self, technique: Tecnica):
19
+ self.technique = technique
20
+
21
+ def setProductsNoSave(self):
22
+ self.list_product = []
23
+ for code in self.list_codes:
24
+ product = Producto(
25
+ codigoProducto=code,
26
+ id_tecnica=self.technique
27
+ )
28
+ self.list_product.append(product)
29
+
30
+ def saveProducts(self):
31
+ if not self.list_product:
32
+ return controller_error("no se han establecido los productos para guardar")
33
+
34
+ try:
35
+ for product in self.list_product:
36
+ product.save()
37
+ return self.list_product
38
+ except DatabaseError as error:
39
+ return controller_error("error al guardar los productos")
tecnicas/controllers/sesion_controller.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.db import DatabaseError
2
+ from ..models import Tecnica, Presentador, SesionSensorial
3
+ from ..utils import controller_error
4
+
5
+
6
+ class SesionController():
7
+ def __init__(self, name_session: str, technique: Tecnica, creator: Presentador):
8
+ self.name_session = name_session or None
9
+ self.technique = technique or None
10
+ self.presenter = creator or None
11
+
12
+ def setData(self, name_session: str, technique: Tecnica, creator: Presentador):
13
+ self.name_session = name_session
14
+ self.technique = technique
15
+ self.presenter = creator
16
+
17
+ def setSession(self):
18
+ if not self.presenter:
19
+ return controller_error("se requiere presentador para crear sesion")
20
+ elif not self.technique:
21
+ return controller_error("se requiere tecnica para crear sesion")
22
+
23
+ self.sensorial_session = SesionSensorial(
24
+ tecnica=self.technique,
25
+ creadoPor=self.presenter,
26
+ )
27
+
28
+ if self.name_session != "":
29
+ self.sensorial_session.nombre_sesion = self.name_session
30
+
31
+ return self.sensorial_session
32
+
33
+ def saveSession(self):
34
+ if not self.sensorial_session:
35
+ return controller_error("no se ha definido la sesion a guardar")
36
+
37
+ try:
38
+ self.sensorial_session.save()
39
+ return self.sensorial_session
40
+ except DatabaseError as error:
41
+ return controller_error("Error al crear la session sensorial")
tecnicas/controllers/tecnica_controller.py CHANGED
@@ -1,16 +1,40 @@
1
- from ..models import TipoTecnica, CategoriaTecnica, Tecnica
 
 
2
 
3
 
4
  class TecnicaController():
5
  def setTechnique(self, **kwargs):
6
- self.technique = Tecnica.objects.create(
7
  tipo_tecnica=kwargs["tipo_tecnica"],
 
8
  repeticiones_max=kwargs["numero_repeticiones"] or 0,
9
  limite_catadores=kwargs["numero_catadores"],
10
  instrucciones=kwargs["instrucciones"],
11
- id_estilo=kwargs["estilo_palabras"],
12
  )
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  @staticmethod
15
  def getTypesTechnique():
16
  showTecnicas = {}
 
1
+ from ..models import TipoTecnica, CategoriaTecnica, Tecnica, EstiloPalabra
2
+ from django.db import DatabaseError
3
+ from ..utils import controller_error
4
 
5
 
6
  class TecnicaController():
7
  def setTechnique(self, **kwargs):
8
+ self.technique = Tecnica(
9
  tipo_tecnica=kwargs["tipo_tecnica"],
10
+ id_estilo=kwargs["estilo_palabras"],
11
  repeticiones_max=kwargs["numero_repeticiones"] or 0,
12
  limite_catadores=kwargs["numero_catadores"],
13
  instrucciones=kwargs["instrucciones"],
 
14
  )
15
 
16
+ def setTechniqueFromBasicData(self, basic):
17
+ self.technique = Tecnica(
18
+ tipo_tecnica=TipoTecnica.objects.get(id=basic["id_tecnica"]),
19
+ id_estilo=EstiloPalabra.objects.get(id=basic["estilo_palabras"]),
20
+ repeticiones_max=basic["numero_repeticiones"] or 0,
21
+ limite_catadores=basic["numero_catadores"],
22
+ instrucciones=basic["instrucciones"] or "Espere instrucciones del Presentador",
23
+ )
24
+
25
+ def getDataTechnique(self):
26
+ return self.technique.toDict()
27
+
28
+ def saveTechnique(self):
29
+ try:
30
+ self.technique.save()
31
+ return self.technique
32
+ except DatabaseError:
33
+ return controller_error("No se ha podido guardar la tecnica")
34
+
35
+ def deleteTechnique(self):
36
+ self.technique.delete()
37
+
38
  @staticmethod
39
  def getTypesTechnique():
40
  showTecnicas = {}
tecnicas/forms/__init__.py CHANGED
@@ -2,4 +2,5 @@ from .sesion_basic_form import SesionBasicForm
2
  from .sesion_tags_form import SesionTagsForm
3
  from .etiqueta_form import EtiquetaForm
4
  from .codes_form import CodesForm
5
- from .catador_form import CatadorForm
 
 
2
  from .sesion_tags_form import SesionTagsForm
3
  from .etiqueta_form import EtiquetaForm
4
  from .codes_form import CodesForm
5
+ from .catador_form import CatadorForm
6
+ from .word_form import WordForm
tecnicas/forms/word_form.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django import forms
2
+
3
+ from ..models import Palabra
4
+
5
+
6
+ class WordForm(forms.ModelForm):
7
+ nombre_palabra = forms.CharField(
8
+ min_length=3,
9
+ max_length=255,
10
+ error_messages={
11
+ "required": "parametros requeridos",
12
+ "unique": "palabra repetida", # Ojo: 'unique' lo maneja ModelForm, no aquí
13
+ "min_length": "la palabra es muy corta",
14
+ "max_length": "la palabra es muy larga",
15
+ },
16
+ widget=forms.TextInput(attrs={
17
+ "class": "bg-gray-300 border-b text-center text-black pb-1 rounded",
18
+ "placeholder": "Ingrese palabra",
19
+ "oninput": "this.value = this.value.toLowerCase()",
20
+ })
21
+ )
22
+
23
+ class Meta:
24
+ model = Palabra
25
+ fields = ["nombre_palabra"]
26
+
27
+ def clean_nombre_palabra(self):
28
+ nombre_palabra = self.cleaned_data.get('nombre_palabra')
29
+
30
+ if nombre_palabra:
31
+ nombre_palabra = nombre_palabra.lower().strip()
32
+
33
+ if Palabra.objects.filter(nombre_palabra=nombre_palabra).exists():
34
+ raise forms.ValidationError("palabra repetida")
35
+
36
+ return nombre_palabra
tecnicas/migrations/0012_alter_sesionsensorial_codigo_sesion_and_more.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-09-08 22:21
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', '0011_alter_escala_tecnica_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.AlterField(
21
+ model_name='sesionsensorial',
22
+ name='tecnica',
23
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='sesion_tecnica', to='tecnicas.tecnica'),
24
+ ),
25
+ ]
tecnicas/migrations/0013_alter_sesionsensorial_codigo_sesion_and_more.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-09-09 21: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', '0012_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
+ migrations.AlterField(
20
+ model_name='sesionsensorial',
21
+ name='fechaCreacion',
22
+ field=models.DateTimeField(auto_created=True),
23
+ ),
24
+ ]
tecnicas/migrations/0014_alter_sesionsensorial_codigo_sesion_and_more.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Generated by Django 5.2.1 on 2025-09-10 00:04
2
+
3
+ import django.utils.timezone
4
+ import shortuuid.main
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('tecnicas', '0013_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.AlterField(
21
+ model_name='sesionsensorial',
22
+ name='fechaCreacion',
23
+ field=models.DateTimeField(default=django.utils.timezone.now),
24
+ ),
25
+ ]
tecnicas/models/escala.py CHANGED
@@ -9,4 +9,4 @@ class Escala(models.Model):
9
  tecnica = models.OneToOneField(Tecnica, on_delete=models.CASCADE, related_name="escala_tecnica")
10
 
11
  def __str__(self):
12
- return self.longitud
 
9
  tecnica = models.OneToOneField(Tecnica, on_delete=models.CASCADE, related_name="escala_tecnica")
10
 
11
  def __str__(self):
12
+ return self.id_tipo_escala.nombre_escala
tecnicas/models/etiquetas_escala.py CHANGED
@@ -1,11 +1,16 @@
1
  from django.db import models
2
-
3
  from .escala import Escala
4
  from .etiqueta import Etiqueta
5
 
 
6
  class EtiquetasEscala(models.Model):
7
- id_escala = models.ForeignKey(Escala, on_delete=models.CASCADE, related_name="escalas_etiqutas_escala")
 
 
 
 
 
 
8
 
9
- id_etiqueta = models.ForeignKey(Etiqueta, on_delete=models.CASCADE, related_name="etiqueta_etiquetas_escala")
10
-
11
- posicion = models.IntegerField()
 
1
  from django.db import models
 
2
  from .escala import Escala
3
  from .etiqueta import Etiqueta
4
 
5
+
6
  class EtiquetasEscala(models.Model):
7
+ id_escala = models.ForeignKey(
8
+ Escala, on_delete=models.CASCADE, related_name="escalas_etiqutas_escala")
9
+
10
+ id_etiqueta = models.ForeignKey(
11
+ Etiqueta, on_delete=models.CASCADE, related_name="etiqueta_etiquetas_escala")
12
+
13
+ posicion = models.IntegerField()
14
 
15
+ def __str__(self):
16
+ return f"Escala {self.id_escala.id}, {self.id_etiqueta.valor_etiqueta}, posicion {self.posicion}"
 
tecnicas/models/orden.py CHANGED
@@ -8,7 +8,13 @@ class Orden(models.Model):
8
  id_tecnica = models.ForeignKey(Tecnica, on_delete=models.CASCADE, related_name="orden_tecnica")
9
  id_catador = models.ForeignKey(Catador, on_delete=models.CASCADE, related_name="orden_catador", null=True)
10
 
 
 
 
11
  class Posicion(models.Model):
12
  id_producto = models.ForeignKey(Producto, on_delete=models.CASCADE, related_name="posicion_producto")
13
  id_orden = models.ForeignKey(Orden, on_delete=models.CASCADE, related_name="posicion_orden")
14
- posicion = models.IntegerField()
 
 
 
 
8
  id_tecnica = models.ForeignKey(Tecnica, on_delete=models.CASCADE, related_name="orden_tecnica")
9
  id_catador = models.ForeignKey(Catador, on_delete=models.CASCADE, related_name="orden_catador", null=True)
10
 
11
+ def __str__(self):
12
+ return f"Orden ID: {self.id}"
13
+
14
  class Posicion(models.Model):
15
  id_producto = models.ForeignKey(Producto, on_delete=models.CASCADE, related_name="posicion_producto")
16
  id_orden = models.ForeignKey(Orden, on_delete=models.CASCADE, related_name="posicion_orden")
17
+ posicion = models.IntegerField()
18
+
19
+ def __str__(self):
20
+ return f"Posicion ID: {self.id}"
tecnicas/models/sesion_sensorial.py CHANGED
@@ -1,16 +1,20 @@
1
- import shortuuid
2
  from django.db import models
3
-
4
  from .presentador import Presentador
5
  from .tecnica import Tecnica
 
 
6
 
7
  class SesionSensorial(models.Model):
8
- codigo_sesion = models.CharField(primary_key=True, default=shortuuid.uuid, editable=False, max_length=22)
 
9
  nombre_sesion = models.CharField(max_length=255, null=True)
10
- fechaCreacion = models.DateTimeField("date published")
11
  activo = models.BooleanField(default=False)
12
- creadoPor = models.ForeignKey(Presentador, on_delete=models.CASCADE, related_name="presentador_sesion")
13
- tecnica = models.ForeignKey(Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
 
 
14
 
15
  def __str__(self):
16
- return self.codigo_sesion
 
 
1
  from django.db import models
2
+ from django.utils import timezone
3
  from .presentador import Presentador
4
  from .tecnica import Tecnica
5
+ import shortuuid
6
+
7
 
8
  class SesionSensorial(models.Model):
9
+ codigo_sesion = models.CharField(
10
+ primary_key=True, default=shortuuid.uuid, editable=False, max_length=22)
11
  nombre_sesion = models.CharField(max_length=255, null=True)
12
+ fechaCreacion = models.DateTimeField(default=timezone.now)
13
  activo = models.BooleanField(default=False)
14
+ creadoPor = models.ForeignKey(
15
+ Presentador, on_delete=models.CASCADE, related_name="presentador_sesion")
16
+ tecnica = models.OneToOneField(
17
+ Tecnica, on_delete=models.CASCADE, related_name="sesion_tecnica")
18
 
19
  def __str__(self):
20
+ return self.codigo_sesion
tecnicas/models/tecnica.py CHANGED
@@ -3,13 +3,26 @@ from django.db import models
3
  from .estilo_palabra import EstiloPalabra
4
  from .tipo_tecnica import TipoTecnica
5
 
 
6
  class Tecnica(models.Model):
7
- tipo_tecnica = models.ForeignKey(TipoTecnica, on_delete=models.CASCADE, related_name="tecnica_tipo_tecnica")
 
8
  repeticiones_max = models.IntegerField(default=0)
9
  repecion = models.IntegerField(default=0)
10
  limite_catadores = models.IntegerField()
11
  instrucciones = models.CharField(max_length=255)
12
- id_estilo = models.ForeignKey(EstiloPalabra, on_delete=models.CASCADE, related_name="estilo_tecnica")
 
13
 
14
  def __str__(self):
15
- return self.tipo_tecnica
 
 
 
 
 
 
 
 
 
 
 
3
  from .estilo_palabra import EstiloPalabra
4
  from .tipo_tecnica import TipoTecnica
5
 
6
+
7
  class Tecnica(models.Model):
8
+ tipo_tecnica = models.ForeignKey(
9
+ TipoTecnica, on_delete=models.CASCADE, related_name="tecnica_tipo_tecnica")
10
  repeticiones_max = models.IntegerField(default=0)
11
  repecion = models.IntegerField(default=0)
12
  limite_catadores = models.IntegerField()
13
  instrucciones = models.CharField(max_length=255)
14
+ id_estilo = models.ForeignKey(
15
+ EstiloPalabra, on_delete=models.CASCADE, related_name="estilo_tecnica")
16
 
17
  def __str__(self):
18
+ return f"{self.id} : {self.tipo_tecnica.nombre_tecnica} : {self.id_estilo.nombre_estilo}"
19
+
20
+ def toDict(self):
21
+ return {
22
+ "tipo_tecnica": self.tipo_tecnica,
23
+ "repeticiones_max": self.repeticiones_max,
24
+ "repecion": self.repecion,
25
+ "limite_catadores": self.limite_catadores,
26
+ "instrucciones": self.instrucciones,
27
+ "id_estilo": self.id_estilo,
28
+ }
tecnicas/static/img/list.svg ADDED
tecnicas/static/img/lupa.svg ADDED
tecnicas/static/js/create-session.js ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const formSubmit = document.querySelector(".ct-cretae-session-form");
2
+
3
+ document.addEventListener("DOMContentLoaded", () => {
4
+ cretaeSession();
5
+ });
6
+
7
+ async function cretaeSession() {
8
+ const dataForm = new FormData(formSubmit);
9
+ const url = `creando-sesion`;
10
+
11
+ try {
12
+ const respone = await fetch(url, {
13
+ method: "POST",
14
+ headers: {
15
+ "X-CSRFToken": dataForm.get("csrfmiddlewaretoken"),
16
+ },
17
+ body: dataForm,
18
+ });
19
+
20
+ const jsonResponse = await respone.json();
21
+
22
+ if (jsonResponse.error) {
23
+ renderElementsResponse({ error: jsonResponse.error });
24
+ return;
25
+ }
26
+
27
+ const data = jsonResponse["data"];
28
+ renderElementsResponse({
29
+ sessionId: data["codigo_sesion"],
30
+ nameSession: data["nombre_sesion"],
31
+ });
32
+ } catch (error) {
33
+ renderElementsResponse({ error: jsonResponse.error });
34
+ }
35
+ }
36
+
37
+ function renderElementsResponse({
38
+ sessionId = "No id",
39
+ error = false,
40
+ nameSession = "",
41
+ }) {
42
+ const container = document.querySelector(".ct-message-create");
43
+ const loadElement = document.querySelector(".ct-load-create");
44
+
45
+ const message = document.createElement("p");
46
+ message.classList.add("text-2xl", "text-white", "text-center", "font-bold");
47
+
48
+ const nameSessionP = document.createElement("p");
49
+ nameSessionP.classList.add(
50
+ "text-lg",
51
+ "text-white",
52
+ "text-center",
53
+ "font-bold"
54
+ );
55
+
56
+ const idSession = document.createElement("p");
57
+ idSession.classList.add("text-lg", "text-white", "text-center", "font-bold");
58
+ const pHelp = document.createElement("p");
59
+
60
+ // ******************************** //
61
+ // Mostrar el error que ha ocurrido //
62
+ // ******************************** //
63
+ if (error) {
64
+ message.textContent = "No se ha podido completar la creacion de la sesion";
65
+ idSession.textContent = `Error: ${error}`;
66
+ } else {
67
+ message.textContent = "La session se ha creado";
68
+ idSession.innerHTML = `El ID de la seesion es:<br><strong class="border-b border-white">${sessionId}</strong>`;
69
+ nameSessionP.textContent = `Nombre de sesion: ${nameSession}`;
70
+
71
+ pHelp.classList.add("text-lg", "text-white", "text-center");
72
+ pHelp.textContent =
73
+ "Puedes pasar este ID a los catadores para que ingresen a la sesion";
74
+ }
75
+
76
+ const divBtns = document.createElement("div");
77
+ divBtns.classList.add(
78
+ "flex",
79
+ "flex-wrap",
80
+ "justify-center",
81
+ "items-center",
82
+ "gap-5"
83
+ );
84
+
85
+ const stylesBtns = [
86
+ "uppercase",
87
+ "text-lg",
88
+ "tracking-wider",
89
+ "font-medium",
90
+ "p-2",
91
+ "px-4",
92
+ "border-b-2",
93
+ "active:border-b-0",
94
+ "active:border-t-2",
95
+ "transition-all",
96
+ "rounded-xl",
97
+ "w-fit",
98
+ ];
99
+
100
+ const aIndex = document.createElement("a");
101
+ aIndex.href = "/cata";
102
+ aIndex.textContent = "Volver al inicio";
103
+
104
+ aIndex.classList.add(
105
+ ...stylesBtns,
106
+ "active:border-blue-500",
107
+ "border-blue-800",
108
+ "bg-blue-500",
109
+ "text-white"
110
+ );
111
+
112
+ const aMonitor = document.createElement("a");
113
+ aMonitor.href = "/cata";
114
+ aMonitor.textContent = "Monitorear la sesion";
115
+
116
+ aMonitor.classList.add(
117
+ ...stylesBtns,
118
+ "active:border-yellow-500",
119
+ "border-yellow-800",
120
+ "bg-yellow-500",
121
+ "text-black"
122
+ );
123
+
124
+ divBtns.appendChild(aIndex);
125
+ if (!error) {
126
+ divBtns.appendChild(aMonitor);
127
+ }
128
+
129
+ loadElement.classList.add("hidden");
130
+ container.classList.remove("hidden");
131
+
132
+ container.innerHTML = "";
133
+
134
+ container.appendChild(message);
135
+ if (nameSession != "") {
136
+ container.appendChild(nameSessionP);
137
+ }
138
+ container.appendChild(idSession);
139
+ container.appendChild(pHelp);
140
+ container.appendChild(divBtns);
141
+ }
tecnicas/static/js/panel-codes.js CHANGED
@@ -9,10 +9,20 @@ let sortData;
9
  initIU();
10
 
11
  function submitData(e) {
 
 
 
 
 
 
 
 
 
 
12
  const codesInput = document.createElement("input");
13
  codesInput.type = "hidden";
14
  codesInput.name = "sort_codes";
15
- codesInput.value = JSON.stringify(sortData);
16
  this.appendChild(codesInput);
17
  }
18
 
@@ -110,6 +120,9 @@ function shuffle(arr) {
110
  return arrSuffle;
111
  }
112
 
 
 
 
113
  function addIUPermutations() {
114
  const container = document.getElementsByClassName("ct-orden-list")[0];
115
  const itemsChild = [];
 
9
  initIU();
10
 
11
  function submitData(e) {
12
+ const submitData = [];
13
+
14
+ sortData.forEach((order) => {
15
+ const simplePosition = {};
16
+ order.forEach((dataPosition) => {
17
+ simplePosition[dataPosition["code"]] = dataPosition["position"];
18
+ });
19
+ submitData.push(simplePosition);
20
+ });
21
+
22
  const codesInput = document.createElement("input");
23
  codesInput.type = "hidden";
24
  codesInput.name = "sort_codes";
25
+ codesInput.value = JSON.stringify(submitData);
26
  this.appendChild(codesInput);
27
  }
28
 
 
120
  return arrSuffle;
121
  }
122
 
123
+ /**
124
+ * Render the UI with permutations
125
+ */
126
  function addIUPermutations() {
127
  const container = document.getElementsByClassName("ct-orden-list")[0];
128
  const itemsChild = [];
tecnicas/static/js/panel-words.js CHANGED
@@ -1,68 +1,272 @@
1
- const selectWordsContainer = document.querySelector(".ct-words-select");
2
- const tagsWords = document.querySelectorAll(".word");
3
- let numberWords = 3;
4
- const allWords = [];
5
-
6
- for (let i = 0; i < tagsWords.length; i++) {
7
- let atributes = tagsWords.item(i).children;
8
- allWords.push({
9
- id: atributes[1].innerText,
10
- name: atributes[0].innerText,
11
- selected: false,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  });
13
- }
14
 
15
- for (let i = 1; i <= numberWords; i++) {
16
- const select = creadSelect(i);
17
- selectWordsContainer.appendChild(select);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
 
20
- function creadSelect(index) {
21
- const label = document.createElement("label");
22
- label.attributes["form"] = `palabra-${index}`;
23
- label.classList.add(
24
- "font-medium",
25
- "py-2",
26
- "px-3",
27
- "bg-gray-200",
28
- "capitalize",
29
  "rounded",
30
- "text-center"
 
 
 
 
 
 
 
 
 
 
 
31
  );
32
 
33
- const p = document.createElement("p");
34
- p.innerText = `Palabra ${index}`;
 
35
 
36
- const select = document.createElement("select");
37
- select.attributes["id"] = `palabra-${index}`;
38
- select.name = `palabra-${index}`;
39
- select.required = true;
40
- select.classList.add(
41
- "ct-select-op",
42
- "p-1",
43
- "rounded",
44
- "bg-gray-500",
45
- "[*]:capitalize",
46
- "[*]:text-white"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  );
48
 
49
- const emtpyOption = document.createElement("option");
50
- emtpyOption.value = "";
51
- emtpyOption.innerText = "Seleccione palabra";
52
- emtpyOption.attributes["selected"] = true;
53
- emtpyOption.attributes["disabled"] = true;
 
54
 
55
- select.appendChild(emtpyOption);
 
 
 
 
56
 
57
- allWords.forEach((dataWord, i) => {
58
- const option = document.createElement("option");
59
- option.value = dataWord.id;
60
- option.innerText = dataWord.name;
61
- select.appendChild(option);
 
 
62
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- label.appendChild(p);
65
- label.appendChild(select);
 
 
 
 
66
 
67
- return label;
68
  }
 
1
+ // **************************************
2
+ // Logic for get to words and render UI
3
+ // **************************************
4
+ const imgList = document.querySelector(".ct-img-list");
5
+
6
+ const listWordsSelect = [];
7
+ const wordsSelectContainer = document.querySelector(".ct-palabras-usadas");
8
+
9
+ const errorp = document.getElementsByClassName("ct-error-words")[0];
10
+ const foundWordsContainer = document.getElementsByClassName(
11
+ "ct-palabras-encontradas"
12
+ )[0];
13
+
14
+ const formSearch = document.getElementsByClassName("ct-serach-words")[0];
15
+ formSearch.addEventListener("submit", getWordsByName);
16
+
17
+ checInitWords();
18
+
19
+ async function getWordsByName(e) {
20
+ e.preventDefault();
21
+ errorp.classList.add("hidden");
22
+ foundWordsContainer.innerHTML = "";
23
+
24
+ let words = [];
25
+
26
+ const dataForm = new FormData(this);
27
+ const params = new URLSearchParams({
28
+ palabra: dataForm.get("search").trim(),
29
  });
 
30
 
31
+ const url = `api/palabras?${params}`;
32
+
33
+ try {
34
+ const respone = await fetch(url, {
35
+ method: "GET",
36
+ headers: {
37
+ "X-CSRFToken": dataForm.get("csrfmiddlewaretoken"),
38
+ },
39
+ });
40
+
41
+ const jsonResponse = await respone.json();
42
+
43
+ if (jsonResponse.error) {
44
+ errorp.textContent = `Error: ${jsonResponse.error}`;
45
+ errorp.classList.remove("hidden");
46
+ return;
47
+ }
48
+
49
+ words = jsonResponse["data"];
50
+ } catch (error) {
51
+ console.log("Error:", error);
52
+ }
53
+
54
+ showWordsFound(words);
55
  }
56
 
57
+ function createWordElement({ word, add = true, callback = null }) {
58
+ const li = document.createElement("li");
59
+ li.classList.add(
60
+ add ? "bg-gray-600" : "bg-gray-400",
61
+ add ? "text-white" : "text-black",
 
 
 
 
62
  "rounded",
63
+ "font-bold",
64
+ "text-lg",
65
+ "px-4",
66
+ "py-3",
67
+ "flex",
68
+ "flex-wrap",
69
+ "flex-row",
70
+ "flex-1",
71
+ "min-w-fit",
72
+ "justify-center",
73
+ "items-center",
74
+ "gap-3"
75
  );
76
 
77
+ const pName = document.createElement("p");
78
+ pName.textContent = word.nombre_palabra;
79
+ pName.id = word.id;
80
 
81
+ let callButton;
82
+
83
+ if (callback) {
84
+ callButton = () => callback(word);
85
+ }
86
+
87
+ li.appendChild(pName);
88
+ li.appendChild(createButton(add, callButton));
89
+
90
+ return li;
91
+ }
92
+
93
+ function createButton(add = true, callback = null) {
94
+ const button = document.createElement("button");
95
+ button.textContent = add ? "➕" : "➖";
96
+ button.classList.add(
97
+ "px-4",
98
+ "border-b-2",
99
+ "active:border-b-0",
100
+ "active:border-t-2",
101
+ "transition-all",
102
+ "rounded-xl",
103
+ "font-black",
104
+ "w-fit",
105
+ "capitalize"
106
+ );
107
+
108
+ button.classList.add(
109
+ add ? "active:border-green-500" : "active:border-red-500",
110
+ add ? "border-green-800" : "border-red-800",
111
+ add ? "bg-green-500" : "bg-red-500"
112
  );
113
 
114
+ if (callback) {
115
+ button.addEventListener("click", callback);
116
+ }
117
+
118
+ return button;
119
+ }
120
 
121
+ function swtichGridSearch(change = true) {
122
+ change
123
+ ? foundWordsContainer.classList.add("grid")
124
+ : foundWordsContainer.classList.remove("grid");
125
+ }
126
 
127
+ function showWordsFound(words) {
128
+ foundWordsContainer.innerHTML = "";
129
+ swtichGridSearch();
130
+
131
+ words.forEach((word) => {
132
+ const elemtnLi = createWordElement({ word: word, callback: addWordToUse });
133
+ foundWordsContainer.appendChild(elemtnLi);
134
  });
135
+ }
136
+
137
+ function addWordToUse(word) {
138
+ if (listWordsSelect.find((w) => w.id === word.id)) {
139
+ spanNotificationRed(
140
+ `La palabra "${word.nombre_palabra}" ya fue seleccionada`
141
+ );
142
+
143
+ return;
144
+ }
145
+
146
+ listWordsSelect.push(word);
147
+ updatelistWordsSelect();
148
+ }
149
+
150
+ function spanNotificationRed(errorMessage) {
151
+ const noti = document.querySelector(".ct-notification-red");
152
+ noti.textContent = errorMessage;
153
+ noti.classList.remove("hidden");
154
+
155
+ setTimeout(() => {
156
+ noti.classList.add("hidden");
157
+ }, 2500);
158
+ }
159
+
160
+ function spanNotificationGreen(message) {
161
+ const noti = document.querySelector(".ct-notification-green");
162
+ noti.textContent = message;
163
+ noti.classList.remove("hidden");
164
+
165
+ setTimeout(() => {
166
+ noti.classList.add("hidden");
167
+ }, 3000);
168
+ }
169
+
170
+ function removeWordToUse(word) {
171
+ const index = listWordsSelect.findIndex((w) => w.id === word.id);
172
+ if (index > -1) {
173
+ listWordsSelect.splice(index, 1);
174
+ updatelistWordsSelect();
175
+ }
176
+ }
177
+
178
+ function updatelistWordsSelect() {
179
+ wordsSelectContainer.innerHTML = "";
180
+
181
+ listWordsSelect.forEach((word) => {
182
+ const elemtnLi = createWordElement({
183
+ word: word,
184
+ add: false,
185
+ callback: removeWordToUse,
186
+ });
187
+ wordsSelectContainer.appendChild(elemtnLi);
188
+ });
189
+
190
+ if (listWordsSelect.length === 0) {
191
+ wordsSelectContainer.appendChild(imgList);
192
+ }
193
+ }
194
+
195
+ // **************************************
196
+ // Logic for post new Word
197
+ // **************************************
198
+ const formNewWord = document.querySelector(".ct-form-new-word");
199
+ formNewWord.addEventListener("submit", postNewWord);
200
+
201
+ async function postNewWord(e) {
202
+ e.preventDefault();
203
+
204
+ const dataForm = new FormData(this);
205
+ const url = "api/palabras";
206
+
207
+ dataForm.set(
208
+ "nombre_palabra",
209
+ dataForm.get("nombre_palabra").trim().toLowerCase()
210
+ );
211
+
212
+ try {
213
+ const respone = await fetch(url, {
214
+ method: "POST",
215
+ headers: {
216
+ "X-CSRFToken": dataForm.get("csrfmiddlewaretoken"),
217
+ },
218
+ body: dataForm,
219
+ });
220
+
221
+ const jsonResponse = await respone.json();
222
+
223
+ if (jsonResponse.error) {
224
+ spanNotificationRed(`Error: ${jsonResponse.error}`);
225
+ return;
226
+ }
227
+
228
+ const word = jsonResponse["data"];
229
+ addWordToUse(word);
230
+ spanNotificationGreen(jsonResponse["message"]);
231
+
232
+ formNewWord.reset();
233
+ } catch (error) {
234
+ spanNotificationRed(`Error: ${error}`);
235
+ }
236
+ }
237
+
238
+ // **************************************
239
+ // Create Session
240
+ // **************************************
241
+ const formCreateSession = document.querySelector(".ct-creat-session");
242
+ formCreateSession.addEventListener("submit", submitSelectWords);
243
+
244
+ async function submitSelectWords(e) {
245
+ e.preventDefault();
246
+
247
+ if (listWordsSelect.length === 0) {
248
+ spanNotificationRed("Debe seleccionar al menos una palabra");
249
+ return;
250
+ }
251
+
252
+ const wordsInput = document.createElement("input");
253
+ wordsInput.type = "hidden";
254
+ wordsInput.name = "words";
255
+ wordsInput.value = JSON.stringify(listWordsSelect);
256
+ this.appendChild(wordsInput);
257
+
258
+ this.submit();
259
+ }
260
+
261
+ function checInitWords() {
262
+ const wordsSends = document.getElementsByClassName("ct-word-received");
263
 
264
+ for (let i = 0; i < wordsSends.length; i++) {
265
+ const id = parseInt(wordsSends.item(i).id);
266
+ const name = wordsSends.item(i).textContent;
267
+ const newWord = { id: id, nombre_palabra: name };
268
+ listWordsSelect.push(newWord);
269
+ }
270
 
271
+ updatelistWordsSelect();
272
  }
tecnicas/templates/tecnicas/create_sesion/configuracion-panel-codes.html CHANGED
@@ -17,7 +17,7 @@
17
  <p class="text-center font-bold tracking-wide text-xl bg-gray-500 text-white px-3 py-3 pb-4 rounded">
18
  Puede dejar los campos como están para usar los códigos predefinidos.
19
  </p>
20
- {% for field in form_worlds %}
21
  <label for="{{ field.id_for_label }}"
22
  class="text-lg flex flex-col items-center px-2 font-medium tracking-wide">
23
  <p class="tracking-normal capitalize text-base font-bold">{{ field.label }}</p>
 
17
  <p class="text-center font-bold tracking-wide text-xl bg-gray-500 text-white px-3 py-3 pb-4 rounded">
18
  Puede dejar los campos como están para usar los códigos predefinidos.
19
  </p>
20
+ {% for field in form_codes %}
21
  <label for="{{ field.id_for_label }}"
22
  class="text-lg flex flex-col items-center px-2 font-medium tracking-wide">
23
  <p class="tracking-normal capitalize text-base font-bold">{{ field.label }}</p>
tecnicas/templates/tecnicas/create_sesion/configuracion-panel-words.html CHANGED
@@ -7,14 +7,6 @@
7
  <article class="w-full flex flex-col justify-center items-center bg-gray-600 my-10">
8
  <section class="w-fit h-fit relative">
9
  <article class="flex flex-col gap-4 bg-gray-400 md:p-10 p-5 rounded-2xl lg:w-4xl w-full">
10
- <ul class="all-words hidden">
11
- {% for word in words %}
12
- <li class="word">
13
- <p>{{ word.nombre_palabra }}</p>
14
- <p>{{ word.id }}</p>
15
- </li>
16
- {% endfor %}
17
- </ul>
18
  <h1 class="text-center font-bold text-4xl">Panel de configuración</h1>
19
  <hr>
20
  {% if error %}
@@ -23,35 +15,94 @@
23
  </p>
24
  {% endif %}
25
 
26
- <article class="flex flex-col md:flex-row gap-5 justify-between items-center">
27
- <section class="flex-1/3 bg-gray-600 text-white p-4 rounded-lg text-center">
28
- <p>Soy una palabra</p>
29
- </section>
30
- <section class="flex-2/3 w-full">
31
- <form action="" method="get" class="h-full w-full">
32
  {% csrf_token %}
33
- <label for="search">
34
- <input type="text" name="search" id="search" placeholder="Buscar palabra"
35
- class="p-2 px-4 bg-gray-200 rounded-lg w-full text-lg">
36
- </label>
 
 
 
 
 
 
 
 
 
37
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  </section>
39
  </article>
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- <form action="" method="post" class="flex flex-col gap-5">
43
- {% csrf_token %}
44
- <article class="p-4 flex flex-col items-center gap-2 rounded">
45
- <h2 class="text-2xl mb-2 text-center font-bold">Estilo Atributos<br>Seleccion de palabras</h2>
46
- <div class="ct-words-select flex flex-row flex-wrap justify-center items-center gap-5 text-lg">
47
- </div>
48
- <hr>
49
- <button type="button"
50
- class="ct-gen-or text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-blue-500 border-blue-800 transition-all rounded-xl bg-blue-500 text-white w-fit capitalize">
51
- Agregar otra palabra
52
  </button>
53
- </article>
54
- </form>
 
 
 
 
 
 
 
55
  </article>
56
  </section>
57
  </article>
 
7
  <article class="w-full flex flex-col justify-center items-center bg-gray-600 my-10">
8
  <section class="w-fit h-fit relative">
9
  <article class="flex flex-col gap-4 bg-gray-400 md:p-10 p-5 rounded-2xl lg:w-4xl w-full">
 
 
 
 
 
 
 
 
10
  <h1 class="text-center font-bold text-4xl">Panel de configuración</h1>
11
  <hr>
12
  {% if error %}
 
15
  </p>
16
  {% endif %}
17
 
18
+ <article class="flex flex-col md:flex-row gap-5 justify-between">
19
+ <section class="flex-3/5 flex flex-col gap-4">
20
+ <form action="" method="get" class="ct-serach-words">
 
 
 
21
  {% csrf_token %}
22
+ <p class="text-lg font-bold text-center mb-2 bg-gray-600 p-2 rounded text-white">
23
+ Dejar en blaco para buscar las primeras 10 palabras
24
+ </p>
25
+ <section class="flex flex-row gap-2">
26
+ <label for="search" class="w-full">
27
+ <input type="text" name="search" id="search" placeholder="Buscar palabra"
28
+ class="p-2 px-4 bg-gray-200 rounded-lg w-full text-lg font-medium">
29
+ </label>
30
+ <button type="submit"
31
+ class="text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-blue-500 border-blue-800 transition-all rounded-xl bg-blue-500 text-white w-fit capitalize">
32
+ Buscar
33
+ </button>
34
+ </section>
35
  </form>
36
+ <p
37
+ class="ct-error-words hidden capitalize text-white bg-red-600 text-center text-2xl p-1.5 rounded">
38
+ error</p>
39
+ <ul class="ct-palabras-encontradas gap-2 grid-cols-2 max-h-96 overflow-y-auto">
40
+ <div class="flex justify-center items-center">
41
+ <img src="{% static 'img/lupa.svg' %}" alt="lupa de busqueda" style="width: 50%;">
42
+ </div>
43
+ </ul>
44
+ </section>
45
+ <section class="flex-1/3 bg-gray-600 text-white p-4 rounded-lg text-center min-h-96">
46
+ <p class="text-xl font-bold pb-3">Lista de palabras</p>
47
+ <hr class="mb-3 text-white">
48
+ <ul class="ct-palabras-usadas flex flex-col gap-3">
49
+ {% if not words %}
50
+ <img src="{% static 'img/list.svg' %}" alt="lista de palabras" class="ct-img-list">
51
+ {% endif %}
52
+ {% for word in words %}
53
+ <li
54
+ class="bg-gray-400 text-black rounded font-bold text-lg px-4 py-3 flex flex-wrap flex-row flex-1 min-w-fit justify-center items-center gap-3">
55
+ <p class="ct-word-received" id="{{ word.id }}">{{ word.nombre_palabra }}</p>
56
+ <button
57
+ class="px-4 border-b-2 active:border-b-0 active:border-t-2 transition-all rounded-xl font-black w-fit capitalize active:border-red-500 border-red-800 bg-red-500">➖</button>
58
+ </li>
59
+ {% endfor %}
60
+ </ul>
61
  </section>
62
  </article>
63
 
64
+ <article class="ct-new-tags bg-gray-600 p-5 flex justify-center items-center rounded-2xl">
65
+ <form method="post" action="" class="ct-form-new-word p-3">
66
+ {% csrf_token %}
67
+ <p class="mb-4 text-2xl min-md:text-xl font-bold text-center text-white">
68
+ ¿No encuentras una palabra?
69
+ </p>
70
+ <label for="{{ form_word.nombre_palabra.id_for_label }}"
71
+ class="font-medium text-xl text-white flex flex-wrap justify-center items-center gap-3 mb-4">
72
+ <p class="px-4">Nueva palabra:</p>
73
+ {{ form_word.nombre_palabra }}
74
+ </label>
75
+ <p
76
+ class="ct-error-word mb-4 font-bold bg-red-500 text-white text-md capitalize text-center rounded hidden">
77
+ error
78
+ </p>
79
+ <hr class="text-white">
80
+ <article class="flex justify-center gap-8 mt-4 text-white font-medium tracking-wide">
81
+ <button
82
+ class="text-lg tracking-wider font-medium p-2 px-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 w-fit"
83
+ type="submit">
84
+ Crear y agregar a la lista
85
+ </button>
86
+ </article>
87
+ </form>
88
+ </article>
89
 
90
+ <article>
91
+ <form action="" method="post" class="ct-creat-session flex justify-center mt-4">
92
+ {% csrf_token %}
93
+ <button type="submit" name="start"
94
+ class="text-lg tracking-wider font-medium p-2 px-4 border-b-2 active:border-b-0 active:border-t-2 active:border-yellow-500 border-yellow-800 transition-all rounded-xl bg-yellow-500 text-white w-fit capitalize">
95
+ Crear sesión
 
 
 
 
96
  </button>
97
+ </form>
98
+ </article>
99
+
100
+ <article
101
+ class="ct-notification-red absolute max-md:fixed bottom-0 mb-4 left-1/2 -translate-x-1/2 bg-red-600 text-white p-2 px-4 rounded-lg text-center font-medium hidden uppercase">
102
+ </article>
103
+ <article
104
+ class="ct-notification-green absolute max-md:fixed bottom-0 mb-4 left-1/2 -translate-x-1/2 bg-green-600 text-white p-2 px-4 rounded-lg text-center font-medium hidden uppercase">
105
+ </article>
106
  </article>
107
  </section>
108
  </article>
tecnicas/templates/tecnicas/create_sesion/creando_sesion.html ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'tecnicas/layouts/base.html' %}
2
+ {% load static %}
3
+
4
+ {% block title %}Creando sesion{% endblock %}
5
+
6
+ {% block content %}
7
+ <article class="w-full flex flex-col justify-center items-center bg-gray-600 my-10">
8
+ <article class="flex flex-col gap-4 bg-gray-400 md:p-10 p-5 rounded-2xl lg:w-4xl w-full">
9
+ <h1 class="text-center font-bold text-4xl">Creando Sesion</h1>
10
+ <hr>
11
+ <section class="ct-load-create flex flex-col gap-4 bg-gray-600 p-6 rounded-xl">
12
+ <p class="text-lg text-white text-center font-bold">
13
+ Estamos creando la sesion, por favor espere...
14
+ </p>
15
+ <hr class="text-white">
16
+ <div class="w-full h-4 bg-green-500 rounded-full overflow-hidden">
17
+ <div class="h-full bg-red-400 animate-pulse" style="width: 100%"></div>
18
+ </div>
19
+ </section>
20
+ <section class="ct-message-create flex flex-col gap-4 bg-gray-600 p-6 rounded-xl hidden"></section>
21
+ <form action="" method="post" class="hidden ct-cretae-session-form">
22
+ {% csrf_token %}
23
+ <input type="hidden" name="action" value="create_session">
24
+ </form>
25
+ </article>
26
+ </article>
27
+ {% endblock %}
28
+
29
+ {% block extra_js %}
30
+ <script src="{% static 'js/create-session.js' %}"></script>
31
+ {% endblock %}
tecnicas/test/test_controller_words.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.test import TestCase
2
+ from django.urls import reverse
3
+ from ..controllers import PalabrasController
4
+ from ..models import Palabra
5
+
6
+
7
+ class TestsApiWords(TestCase):
8
+ ids: list[int]
9
+ words_saved: list[Palabra]
10
+
11
+ def setUp(self):
12
+ self.named_words = ["rasposo", "afilado", "poroso"]
13
+ words_to_save = [Palabra(nombre_palabra=name)
14
+ for name in self.named_words]
15
+ self.words_saved = Palabra.objects.bulk_create(words_to_save)
16
+ self.ids = [word.id for word in self.words_saved]
17
+
18
+ def test_get_words_by_list_ids(self):
19
+ instance = PalabrasController(ids=self.ids)
20
+ new_words = Palabra.objects.filter(id__in=self.ids)
21
+ names = [word.nombre_palabra for word in new_words]
22
+ self.assertEqual(set(self.named_words), set(names))
23
+
24
+ def test_get_words_by_list_ids_fail(self):
25
+ instance = PalabrasController(ids=[23, 12, 76])
26
+ response = instance.setWords()
27
+ self.assertIn("error", response)
tecnicas/test/test_words.py CHANGED
@@ -7,9 +7,9 @@ class TestsApiWords(TestCase):
7
  def setUp(self):
8
  Palabra.objects.create(nombre_palabra="salado")
9
 
10
- def test_get_word_fail(self):
11
  word_name = "dulce"
12
- expected_error = "palabra no encontrada"
13
 
14
  response = self.client.get(
15
  reverse("cata_system:api_palabras")+f"?palabra={word_name}"
@@ -21,7 +21,7 @@ class TestsApiWords(TestCase):
21
  self.assertIn("error", res_json)
22
  self.assertEqual(res_json["error"], expected_error)
23
 
24
- def test_get_word_no_parameter(self):
25
  expected_error = "parametros no encontrados"
26
 
27
  response = self.client.get(reverse("cata_system:api_palabras"))
@@ -32,11 +32,12 @@ class TestsApiWords(TestCase):
32
  self.assertIn("error", res_json)
33
  self.assertEqual(res_json["error"], expected_error)
34
 
35
- def test_get_word_success(self):
36
- word_name = "salado"
37
- expected_message = "dato localizado"
38
 
39
- expected_data = Palabra.objects.get(nombre_palabra=word_name).to_dict()
 
40
 
41
  response = self.client.get(
42
  reverse("cata_system:api_palabras")+f"?palabra={word_name}"
@@ -50,19 +51,17 @@ class TestsApiWords(TestCase):
50
  self.assertEqual(res_json["message"], expected_message)
51
 
52
  data = res_json["data"]
53
- self.assertEqual(data["id"], expected_data["id"])
54
- self.assertEqual(data["name"], expected_data["nombre_palabra"])
55
 
56
  def test_post_word_success(self):
57
  word_name = "dulce"
58
  expected_message = "palabra creada"
59
 
60
- response = self.client.post(
61
- reverse("cata_system:api_palabras"),
62
- data={"nombre_palabra": word_name}
63
- )
64
 
65
- self.assertEqual(response.status_code, 200)
66
 
67
  res_json = response.json()
68
 
@@ -74,27 +73,60 @@ class TestsApiWords(TestCase):
74
  self.assertIn("nombre_palabra", data)
75
  self.assertEqual(data["nombre_palabra"], word_name)
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  def test_post_word_fail(self):
78
  word_name = "salado"
79
  expected_error = "palabra repetida"
80
 
 
 
81
  response = self.client.post(
82
  reverse("cata_system:api_palabras"),
83
- data={"nombre_palabra": word_name}
84
  )
85
 
86
- self.assertEqual(response.status_code, 200)
87
-
88
  res_json = response.json()
89
  self.assertIn("error", res_json)
90
  self.assertEqual(res_json["error"], expected_error)
91
-
92
  def test_post_word_no_parameter(self):
93
- expected_error = "parametros no encontrados"
94
 
95
  response = self.client.post(reverse("cata_system:api_palabras"))
96
 
97
- self.assertEqual(response.status_code, 200)
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
  res_json = response.json()
100
  self.assertIn("error", res_json)
 
7
  def setUp(self):
8
  Palabra.objects.create(nombre_palabra="salado")
9
 
10
+ def test_get_words_fail(self):
11
  word_name = "dulce"
12
+ expected_error = "no existen palabras"
13
 
14
  response = self.client.get(
15
  reverse("cata_system:api_palabras")+f"?palabra={word_name}"
 
21
  self.assertIn("error", res_json)
22
  self.assertEqual(res_json["error"], expected_error)
23
 
24
+ def test_get_words_no_parameter(self):
25
  expected_error = "parametros no encontrados"
26
 
27
  response = self.client.get(reverse("cata_system:api_palabras"))
 
32
  self.assertIn("error", res_json)
33
  self.assertEqual(res_json["error"], expected_error)
34
 
35
+ def test_get_words_success(self):
36
+ word_name = "sal"
37
+ expected_message = "datos localizados"
38
 
39
+ expected_data = Palabra.objects.filter(
40
+ nombre_palabra__contains=word_name)
41
 
42
  response = self.client.get(
43
  reverse("cata_system:api_palabras")+f"?palabra={word_name}"
 
51
  self.assertEqual(res_json["message"], expected_message)
52
 
53
  data = res_json["data"]
54
+ self.assertEqual(len(data), len(expected_data))
 
55
 
56
  def test_post_word_success(self):
57
  word_name = "dulce"
58
  expected_message = "palabra creada"
59
 
60
+ data = {
61
+ "nombre_palabra": word_name
62
+ }
 
63
 
64
+ response = self.client.post(reverse("cata_system:api_palabras"), data)
65
 
66
  res_json = response.json()
67
 
 
73
  self.assertIn("nombre_palabra", data)
74
  self.assertEqual(data["nombre_palabra"], word_name)
75
 
76
+ def test_post_word_lowercase(self):
77
+ word_name = "AMargo"
78
+ expected_message = "palabra creada"
79
+
80
+ data = {
81
+ "nombre_palabra": word_name
82
+ }
83
+
84
+ response = self.client.post(reverse("cata_system:api_palabras"), data)
85
+
86
+ res_json = response.json()
87
+
88
+ self.assertIn("message", res_json)
89
+ self.assertEqual(res_json["message"], expected_message)
90
+
91
+ data = res_json["data"]
92
+ self.assertIn("id", data)
93
+ self.assertIn("nombre_palabra", data)
94
+ self.assertEqual(data["nombre_palabra"], word_name.lower())
95
+
96
  def test_post_word_fail(self):
97
  word_name = "salado"
98
  expected_error = "palabra repetida"
99
 
100
+ data = {"nombre_palabra": word_name}
101
+
102
  response = self.client.post(
103
  reverse("cata_system:api_palabras"),
104
+ data
105
  )
106
 
 
 
107
  res_json = response.json()
108
  self.assertIn("error", res_json)
109
  self.assertEqual(res_json["error"], expected_error)
110
+
111
  def test_post_word_no_parameter(self):
112
+ expected_error = "parametros requeridos"
113
 
114
  response = self.client.post(reverse("cata_system:api_palabras"))
115
 
116
+ res_json = response.json()
117
+ self.assertIn("error", res_json)
118
+ self.assertEqual(res_json["error"], expected_error)
119
+
120
+ def test_post_word_too_short(self):
121
+ word_name = "ab"
122
+ expected_error = "la palabra es muy corta"
123
+
124
+ data = {"nombre_palabra": word_name}
125
+
126
+ response = self.client.post(
127
+ reverse("cata_system:api_palabras"),
128
+ data
129
+ )
130
 
131
  res_json = response.json()
132
  self.assertIn("error", res_json)
tecnicas/urls.py CHANGED
@@ -15,6 +15,7 @@ urlpatterns = [
15
  path("panel-configuracion-tags", views.configurationPanelTags, name="panel_configuracion_tags"),
16
  path("panel-configuracion-codes", views.configurationPanelCodes, name="panel_configuracion_codes"),
17
  path("panel-configuracion-words", views.configurationPanelWords, name="panel_configuracion_words"),
 
18
  path("nueva-etiqueta", views.newTag, name="nueva_etiqueta"),
19
  path("crear-catador", views.testerCreate, name="crear_catador"),
20
  path("buscar-catador", views.testerSearch, name="buscar_catador"),
 
15
  path("panel-configuracion-tags", views.configurationPanelTags, name="panel_configuracion_tags"),
16
  path("panel-configuracion-codes", views.configurationPanelCodes, name="panel_configuracion_codes"),
17
  path("panel-configuracion-words", views.configurationPanelWords, name="panel_configuracion_words"),
18
+ path("creando-sesion", views.createSession, name="creando_sesion"),
19
  path("nueva-etiqueta", views.newTag, name="nueva_etiqueta"),
20
  path("crear-catador", views.testerCreate, name="crear_catador"),
21
  path("buscar-catador", views.testerSearch, name="buscar_catador"),
tecnicas/utils/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
  from .code_generate import generarCodigo
2
  from .code_generate import generarCodigos
3
- from .general_error import general_error
 
1
  from .code_generate import generarCodigo
2
  from .code_generate import generarCodigos
3
+ from .personal_errors import general_error, controller_error
tecnicas/utils/{general_error.py → personal_errors.py} RENAMED
@@ -1,5 +1,11 @@
1
  from django.http import JsonResponse
2
 
 
3
  def general_error(message="informacion incorrecta"):
4
- respuesta = { "error" : message }
5
- return JsonResponse(respuesta)
 
 
 
 
 
 
1
  from django.http import JsonResponse
2
 
3
+
4
  def general_error(message="informacion incorrecta"):
5
+ result = {"error": message}
6
+ return JsonResponse(result)
7
+
8
+
9
+ def controller_error(message="ha ocurrido un error inesperado"):
10
+ result = {"error": message}
11
+ return result
tecnicas/views/__init__.py CHANGED
@@ -11,4 +11,5 @@ from .configuration_panel_words import configurationPanelWords
11
  from .api_tag import newTag
12
  from .tester_create import testerCreate
13
  from .tester_search import testerSearch
14
- from .api_words import words
 
 
11
  from .api_tag import newTag
12
  from .tester_create import testerCreate
13
  from .tester_search import testerSearch
14
+ from .api_words import words
15
+ from .create_session import createSession
tecnicas/views/api_words.py CHANGED
@@ -2,39 +2,49 @@ from django.http import HttpRequest, JsonResponse
2
  from django.db import IntegrityError
3
  from ..models import Palabra
4
  from ..utils import general_error
 
5
 
6
 
7
  def words(req: HttpRequest):
8
  if req.method == "GET":
9
  try:
10
  word_name = req.GET["palabra"]
11
- fund_word = Palabra.objects.get(nombre_palabra=word_name)
 
 
 
 
 
12
  except KeyError:
13
  return general_error("parametros no encontrados")
14
- except Palabra.DoesNotExist:
15
- return general_error("palabra no encontrada")
16
 
17
- response = {
18
- "message": "dato localizado",
19
- "data": {
20
- "id": fund_word.id,
21
- "name": fund_word.nombre_palabra,
22
- }
23
- }
24
 
25
- return JsonResponse(response)
26
- elif req.method == "POST":
27
- try:
28
- word_name = req.POST["nombre_palabra"]
29
- new_word = Palabra.objects.create(nombre_palabra=word_name)
30
- except KeyError:
31
- return general_error("parametros no encontrados")
32
- except IntegrityError:
33
- return general_error("palabra repetida")
34
 
35
  response = {
36
- "message": "palabra creada",
37
- "data": new_word.to_dict()
38
  }
39
 
40
  return JsonResponse(response)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  from django.db import IntegrityError
3
  from ..models import Palabra
4
  from ..utils import general_error
5
+ from ..forms.word_form import WordForm
6
 
7
 
8
  def words(req: HttpRequest):
9
  if req.method == "GET":
10
  try:
11
  word_name = req.GET["palabra"]
12
+
13
+ if word_name == "":
14
+ fund_words = Palabra.objects.all()[:10]
15
+ else:
16
+ fund_words = Palabra.objects.filter(
17
+ nombre_palabra__startswith=word_name)
18
  except KeyError:
19
  return general_error("parametros no encontrados")
 
 
20
 
21
+ if not fund_words:
22
+ return general_error("no existen palabras")
 
 
 
 
 
23
 
24
+ response_words = [word.to_dict() for word in fund_words]
 
 
 
 
 
 
 
 
25
 
26
  response = {
27
+ "message": "datos localizados",
28
+ "data": response_words
29
  }
30
 
31
  return JsonResponse(response)
32
+ elif req.method == "POST":
33
+ form = WordForm(req.POST)
34
+
35
+ if form.is_valid():
36
+ form.cleaned_data["nombre_palabra"] = form.cleaned_data["nombre_palabra"].lower(
37
+ )
38
+ new_word = form.save()
39
+ send_word = {
40
+ "nombre_palabra": new_word.nombre_palabra,
41
+ "id": new_word.id
42
+ }
43
+ response = {
44
+ "message": "palabra creada",
45
+ "data": send_word
46
+ }
47
+ return JsonResponse(response)
48
+ else:
49
+ errors = form.errors.get("nombre_palabra")
50
+ return general_error(errors[0])
tecnicas/views/configuration_panel_basic.py CHANGED
@@ -4,7 +4,19 @@ from django.urls import reverse
4
  from ..forms import SesionBasicForm
5
  from ..models import TipoTecnica
6
 
 
7
  def configurationPanelBasic(req: HttpRequest):
 
 
 
 
 
 
 
 
 
 
 
8
  if req.method == "POST":
9
  try:
10
  form = SesionBasicForm(req.POST)
@@ -23,7 +35,7 @@ def configurationPanelBasic(req: HttpRequest):
23
  except KeyError:
24
  return redirect(reverse("cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
25
 
26
- return render(req, "tecnicas/create_sesion/configuracion-panel-basic.html", { "form_sesion": form, "error": "Ha ocurrido un error al continuar al siguiente paso." })
27
  elif req.method == "GET":
28
  try:
29
  id_tecnica = req.GET["id_tecnica"]
@@ -35,6 +47,6 @@ def configurationPanelBasic(req: HttpRequest):
35
 
36
  if tecnica:
37
  form_sesion = SesionBasicForm(id_tecnica_new=id_tecnica)
38
- return render(req, "tecnicas/create_sesion/configuracion-panel-basic.html", { "form_sesion": form_sesion })
39
  else:
40
- return redirect(reverse("cata_system:seleccion_tecnica") + "?error=la_tecnica_no_existe")
 
4
  from ..forms import SesionBasicForm
5
  from ..models import TipoTecnica
6
 
7
+
8
  def configurationPanelBasic(req: HttpRequest):
9
+ keys_forms = [
10
+ "form_basic",
11
+ "form_tags",
12
+ "form_codes",
13
+ "form_words"
14
+ ]
15
+
16
+ for key in keys_forms:
17
+ if key in req.session:
18
+ del req.session[key]
19
+
20
  if req.method == "POST":
21
  try:
22
  form = SesionBasicForm(req.POST)
 
35
  except KeyError:
36
  return redirect(reverse("cata_system:seleccion_tecnica") + "?error=error en datos de configuracion")
37
 
38
+ return render(req, "tecnicas/create_sesion/configuracion-panel-basic.html", {"form_sesion": form, "error": "Ha ocurrido un error al continuar al siguiente paso."})
39
  elif req.method == "GET":
40
  try:
41
  id_tecnica = req.GET["id_tecnica"]
 
47
 
48
  if tecnica:
49
  form_sesion = SesionBasicForm(id_tecnica_new=id_tecnica)
50
+ return render(req, "tecnicas/create_sesion/configuracion-panel-basic.html", {"form_sesion": form_sesion})
51
  else:
52
+ return redirect(reverse("cata_system:seleccion_tecnica") + "?error=la_tecnica_no_existe")
tecnicas/views/configuration_panel_codes.py CHANGED
@@ -7,12 +7,12 @@ import json
7
 
8
 
9
  def configurationPanelCodes(req: HttpRequest):
10
- data_basic = req.session["form_basic"]
11
- data_tags = req.session["form_tags"]
 
 
12
 
13
- if not data_basic or not data_tags:
14
- redirect(reverse("cata_system:seleccion_tecnica") +
15
- "?error=datos del formulario requerido no encontrados")
16
 
17
  num_products = data_basic["numero_productos"]
18
  num_tester = data_basic["numero_catadores"]
@@ -20,40 +20,40 @@ def configurationPanelCodes(req: HttpRequest):
20
  if req.method == "GET":
21
  codes_products = generarCodigos(num_products)
22
 
23
- form_worlds = CodesForm(codes=codes_products)
24
 
25
- context_worlds_form = {
26
- "form_worlds": form_worlds,
27
  "num_tester": num_tester
28
  }
29
 
30
- return render(req, "tecnicas/create_sesion/configuracion-panel-codes.html", context_worlds_form)
31
  elif req.method == "POST":
32
  sorts_code = json.loads(req.POST.get("sort_codes"))
33
  codes = []
34
- context_worlds_form = {}
35
 
36
  for name, value in req.POST.items():
37
  if name.__contains__("producto_"):
38
  codes.append(value)
39
 
40
- form_worlds = CodesForm(req.POST, codes=codes)
41
 
42
- context_worlds_form = {
43
- "form_worlds": form_worlds,
44
  "num_tester": num_tester,
45
  }
46
 
47
- if form_worlds.is_valid():
48
  codes_sort = {"product_codes": []}
49
 
50
- for name, value in form_worlds.cleaned_data.items():
51
  codes_sort["product_codes"].append({name: value})
52
 
53
  codes_sort["sort_codes"] = sorts_code
54
  req.session["form_codes"] = codes_sort
55
  return redirect(reverse("cata_system:panel_configuracion_words"))
56
  else:
57
- context_worlds_form["error"] = "error en los datos recibidos"
58
 
59
- return render(req, "tecnicas/create_sesion/configuracion-panel-codes.html", context_worlds_form)
 
7
 
8
 
9
  def configurationPanelCodes(req: HttpRequest):
10
+ if not req.session.get("form_basic") or not req.session.get("form_tags"):
11
+ req.session.flush()
12
+ return redirect(reverse("cata_system:seleccion_tecnica") +
13
+ "?error=datos del formulario requerido no encontrados")
14
 
15
+ data_basic = req.session["form_basic"]
 
 
16
 
17
  num_products = data_basic["numero_productos"]
18
  num_tester = data_basic["numero_catadores"]
 
20
  if req.method == "GET":
21
  codes_products = generarCodigos(num_products)
22
 
23
+ form_codes = CodesForm(codes=codes_products)
24
 
25
+ context_codes_form = {
26
+ "form_codes": form_codes,
27
  "num_tester": num_tester
28
  }
29
 
30
+ return render(req, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
31
  elif req.method == "POST":
32
  sorts_code = json.loads(req.POST.get("sort_codes"))
33
  codes = []
34
+ context_codes_form = {}
35
 
36
  for name, value in req.POST.items():
37
  if name.__contains__("producto_"):
38
  codes.append(value)
39
 
40
+ form_codes = CodesForm(req.POST, codes=codes)
41
 
42
+ context_codes_form = {
43
+ "form_codes": form_codes,
44
  "num_tester": num_tester,
45
  }
46
 
47
+ if form_codes.is_valid():
48
  codes_sort = {"product_codes": []}
49
 
50
+ for name, value in form_codes.cleaned_data.items():
51
  codes_sort["product_codes"].append({name: value})
52
 
53
  codes_sort["sort_codes"] = sorts_code
54
  req.session["form_codes"] = codes_sort
55
  return redirect(reverse("cata_system:panel_configuracion_words"))
56
  else:
57
+ context_codes_form["error"] = "error en los datos recibidos"
58
 
59
+ return render(req, "tecnicas/create_sesion/configuracion-panel-codes.html", context_codes_form)
tecnicas/views/configuration_panel_tags.py CHANGED
@@ -4,31 +4,34 @@ from django.urls import reverse
4
  from ..forms import SesionTagsForm, EtiquetaForm
5
  from ..models import TipoEscala
6
 
 
7
  def configurationPanelTags(req: HttpRequest):
8
  basic_data = req.session.get("form_basic", {})
9
 
10
  if not basic_data:
11
  return redirect(reverse('cata_system:panel_configuracion_basic'))
12
-
13
  type_scale = TipoEscala.objects.get(pk=basic_data["tipo_escala"])
14
  tamano_escala = basic_data["tamano_escala"]
15
  form_new_etiqueta = EtiquetaForm()
16
 
17
  if req.method == "GET":
18
- form_etiqutas = SesionTagsForm(longitud=tamano_escala, tipo_escala=type_scale.nombre_escala)
 
19
 
20
  context_tags = {
21
- "form_tags":form_etiqutas,
22
  "form_new_tag": form_new_etiqueta
23
  }
24
 
25
  return render(req, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
26
  elif req.method == "POST":
27
  values = {}
28
- form = SesionTagsForm(req.POST, longitud=tamano_escala, tipo_escala=type_scale.nombre_escala)
 
29
 
30
  context_tags = {
31
- "form_tags":form,
32
  "form_new_tag": form_new_etiqueta
33
  }
34
 
@@ -40,4 +43,4 @@ def configurationPanelTags(req: HttpRequest):
40
  return redirect(reverse("cata_system:panel_configuracion_codes"))
41
  else:
42
  context_tags["error"] = "ha ocurrido un error"
43
- return render(req, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
 
4
  from ..forms import SesionTagsForm, EtiquetaForm
5
  from ..models import TipoEscala
6
 
7
+
8
  def configurationPanelTags(req: HttpRequest):
9
  basic_data = req.session.get("form_basic", {})
10
 
11
  if not basic_data:
12
  return redirect(reverse('cata_system:panel_configuracion_basic'))
13
+
14
  type_scale = TipoEscala.objects.get(pk=basic_data["tipo_escala"])
15
  tamano_escala = basic_data["tamano_escala"]
16
  form_new_etiqueta = EtiquetaForm()
17
 
18
  if req.method == "GET":
19
+ form_etiqutas = SesionTagsForm(
20
+ longitud=tamano_escala, tipo_escala=type_scale.nombre_escala)
21
 
22
  context_tags = {
23
+ "form_tags": form_etiqutas,
24
  "form_new_tag": form_new_etiqueta
25
  }
26
 
27
  return render(req, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
28
  elif req.method == "POST":
29
  values = {}
30
+ form = SesionTagsForm(req.POST, longitud=tamano_escala,
31
+ tipo_escala=type_scale.nombre_escala)
32
 
33
  context_tags = {
34
+ "form_tags": form,
35
  "form_new_tag": form_new_etiqueta
36
  }
37
 
 
43
  return redirect(reverse("cata_system:panel_configuracion_codes"))
44
  else:
45
  context_tags["error"] = "ha ocurrido un error"
46
+ return render(req, "tecnicas/create_sesion/configuracion-panel-tags.html", context_tags)
tecnicas/views/configuration_panel_words.py CHANGED
@@ -1,11 +1,44 @@
1
  from django.http import HttpRequest
2
- from django.shortcuts import render
 
3
  from ..models.palabra import Palabra
 
 
 
 
4
 
5
  def configurationPanelWords(req: HttpRequest):
 
 
 
 
 
 
 
 
 
 
6
  if req.method == "GET":
7
- allWords = Palabra.objects.all()
8
- context = {
9
- "words": allWords
10
- }
11
- return render(req, "tecnicas/create_sesion/configuracion-panel-words.html", context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.http import HttpRequest
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
  from ..models.palabra import Palabra
5
+ from ..forms import WordForm
6
+
7
+ import json
8
+
9
 
10
  def configurationPanelWords(req: HttpRequest):
11
+ if not req.session.get("form_basic") or not req.session.get("form_tags") or not req.session.get("form_codes"):
12
+ req.session.flush()
13
+ return redirect(reverse("cata_system:seleccion_tecnica") +
14
+ "?error=datos del formulario requerido no encontrados")
15
+
16
+ form = WordForm()
17
+ context = {
18
+ "form_word": form
19
+ }
20
+
21
  if req.method == "GET":
22
+ return render(req, "tecnicas/create_sesion/configuracion-panel-words.html", context)
23
+ elif req.method == "POST":
24
+ if not req.POST.get("words"):
25
+ return render(req, "tecnicas/create_sesion/configuracion-panel-words.html", context)
26
+
27
+ words = json.loads(req.POST.get("words"))
28
+ context["words"] = words
29
+
30
+ ids_words = [word["id"] for word in words]
31
+
32
+ if len(ids_words) != len(set(ids_words)):
33
+ context["error"] = "existen palabras duplicadas"
34
+ return render(req, "tecnicas/create_sesion/configuracion-panel-words.html", context)
35
+
36
+ exist_words = Palabra.objects.filter(
37
+ id__in=ids_words).count() == len(ids_words)
38
+
39
+ if not exist_words:
40
+ context["error"] = "algunas palabras no existen"
41
+ return render(req, "tecnicas/create_sesion/configuracion-panel-words.html", context)
42
+
43
+ req.session["form_words"] = ids_words
44
+ return redirect(reverse("cata_system:creando_sesion"))
tecnicas/views/create_session.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest, JsonResponse
2
+ from django.shortcuts import render, redirect
3
+ from django.urls import reverse
4
+ from ..utils import general_error
5
+ from ..controllers import TecnicaController, EscalaController, ProductosController, OrdenesController, EstiloPalabrasController, PalabrasController, SesionController
6
+ from ..models import Presentador
7
+
8
+
9
+ def createSession(req: HttpRequest):
10
+ if req.method == "GET":
11
+ return render(req, 'tecnicas/create_sesion/creando_sesion.html')
12
+ if req.method == "POST":
13
+ if req.POST.get('action') == 'create_session':
14
+ if not req.session.get("form_basic") or not req.session.get("form_tags") or not req.session.get("form_codes") or not req.session.get("form_words"):
15
+ req.session.flush()
16
+ return general_error("no se ha especificado informacion necesaria para la creacion de la sesion")
17
+
18
+ # ////////////////////////////////////////////////////// #
19
+ #
20
+ # First step: Create technique and scale with their tags #
21
+ #
22
+ # ////////////////////////////////////////////////////// #
23
+ data_basic = req.session["form_basic"]
24
+ controllerTechnique = TecnicaController()
25
+ controllerTechnique.setTechniqueFromBasicData(basic=data_basic)
26
+
27
+ technique = controllerTechnique.saveTechnique()
28
+ if not technique:
29
+ return general_error("error al guardar la tecnica")
30
+
31
+ data_scale = {
32
+ "id_scale": data_basic["tipo_escala"],
33
+ "size": data_basic["tamano_escala"],
34
+ "technique": technique
35
+ }
36
+
37
+ controllerScale = EscalaController(data=data_scale)
38
+
39
+ scale = controllerScale.saveScale()
40
+ if isinstance(scale, dict):
41
+ controllerTechnique.deleteTechnique()
42
+ return general_error(scale["error"])
43
+
44
+ dict_tags = req.session["form_tags"]
45
+ saved_related_tags = controllerScale.realteTags(dict_tags)
46
+ if "error" in saved_related_tags:
47
+ return general_error(saved_related_tags["error"])
48
+
49
+ # ////////////////////////////////////////////////////////// #
50
+ #
51
+ # Second step: Create orders, productos and set the position #
52
+ #
53
+ # ////////////////////////////////////////////////////////// #
54
+ data_codes = req.session["form_codes"]
55
+
56
+ list_codes_dict = data_codes["product_codes"]
57
+
58
+ codes = []
59
+ for product in list_codes_dict:
60
+ code = next(iter(product.values()))
61
+ codes.append(code)
62
+
63
+ controllerProducts = ProductosController(
64
+ codes=codes,
65
+ technique=technique
66
+ )
67
+
68
+ controllerProducts.setProductsNoSave()
69
+ saved_prodcuts = controllerProducts.saveProducts()
70
+ if isinstance(saved_prodcuts, dict):
71
+ controllerTechnique.deleteTechnique()
72
+ return general_error(saved_prodcuts["error"])
73
+
74
+ raw_sort_codes = data_codes["sort_codes"]
75
+ controllerOrdes = OrdenesController(
76
+ raw_orders=raw_sort_codes,
77
+ list_products=saved_prodcuts,
78
+ technique=technique
79
+ )
80
+
81
+ controllerOrdes.setOrdersToSave()
82
+ saved_orders = controllerOrdes.saveOrders()
83
+ if isinstance(saved_orders, dict):
84
+ controllerTechnique.deleteTechnique()
85
+ return general_error(saved_orders["error"])
86
+
87
+ seded_positions = controllerOrdes.setPositions()
88
+ if isinstance(seded_positions, dict):
89
+ controllerTechnique.deleteTechnique()
90
+ return general_error(seded_positions["error"])
91
+
92
+ saved_postions = controllerOrdes.savePositions()
93
+ if isinstance(saved_postions, dict):
94
+ controllerTechnique.deleteTechnique()
95
+ return general_error(saved_prodcuts["error"])
96
+
97
+ # /////////////////////////////////////////////////////// #
98
+ #
99
+ # Third step: Create relations technique with Words Style #
100
+ #
101
+ # /////////////////////////////////////////////////////// #
102
+ ids_words = req.session["form_words"]
103
+ words_controller = PalabrasController(ids=ids_words)
104
+
105
+ words_to_use = words_controller.setWords()
106
+ if isinstance(words_to_use, dict):
107
+ controllerTechnique.deleteTechnique()
108
+ return general_error(words_to_use["error"])
109
+
110
+ style_controller = EstiloPalabrasController(
111
+ technique=technique, words=words_to_use)
112
+
113
+ instace_style = style_controller.createAndSaveInstaceStyle()
114
+ if isinstance(instace_style, dict):
115
+ controllerTechnique.deleteTechnique()
116
+ return general_error(instace_style["error"])
117
+
118
+ words_using = style_controller.relatedWords()
119
+ if isinstance(words_using, dict):
120
+ controllerTechnique.deleteTechnique()
121
+ return general_error("error")
122
+
123
+ # //////////////////////////////////////////////////////// #
124
+ #
125
+ # Fourth step: Create session and relat with the technique #
126
+ #
127
+ # //////////////////////////////////////////////////////// #
128
+ session_controller = SesionController(
129
+ name_session=data_basic["nombre_sesion"] if data_basic["nombre_sesion"] != "" else None,
130
+ technique=technique,
131
+ creator=Presentador.objects.get(nombre_usuario="aguBido")
132
+ )
133
+
134
+ setting_session = session_controller.setSession()
135
+ if isinstance(setting_session, dict):
136
+ controllerTechnique.deleteTechnique()
137
+ return general_error(setting_session["error"])
138
+
139
+ saved_session = session_controller.saveSession()
140
+ if isinstance(saved_session, dict):
141
+ return general_error(saved_session["error"])
142
+
143
+ context = {
144
+ "message": "sesion creada",
145
+ "data": {
146
+ "codigo_sesion": saved_session.codigo_sesion,
147
+ "nombre_sesion": saved_session.nombre_sesion
148
+ }
149
+ }
150
+
151
+ keys_forms = [
152
+ "form_basic",
153
+ "form_tags",
154
+ "form_codes",
155
+ "form_words"
156
+ ]
157
+
158
+ for key in keys_forms:
159
+ if key in req.session:
160
+ del req.session[key]
161
+
162
+ return JsonResponse(context)
163
+ return general_error("ha orcurrido un error inesperado")