chartManD commited on
Commit
934cca5
·
1 Parent(s): 7baf813

Guardado de progreso con nappging

Browse files
tecnicas/admin.py CHANGED
@@ -1,6 +1,6 @@
1
  from django.contrib import admin
2
 
3
- from .models import CategoriaTecnica, TipoTecnica, TipoEscala, EstiloPalabra, Catador, Presentador, Tecnica, SesionSensorial, EsAtributo, Palabra, Vocabulario, Etiqueta, Escala, EtiquetasEscala, Producto, Participacion, Orden, Posicion, Dato, ValorDecimal, ValorBooleano, Calificacion, ListaPalabras, GrupoProducto, Modalidad
4
 
5
  # Register your models here.
6
  admin.site.register(CategoriaTecnica)
@@ -32,6 +32,11 @@ admin.site.register(Dato)
32
  admin.site.register(ValorDecimal)
33
  admin.site.register(ValorBooleano)
34
  admin.site.register(Calificacion)
 
35
  admin.site.register(ListaPalabras)
36
  admin.site.register(GrupoProducto)
 
37
  admin.site.register(Modalidad)
 
 
 
 
1
  from django.contrib import admin
2
 
3
+ from .models import CategoriaTecnica, TipoTecnica, TipoEscala, EstiloPalabra, Catador, Presentador, Tecnica, SesionSensorial, EsAtributo, Palabra, Vocabulario, Etiqueta, Escala, EtiquetasEscala, Producto, Participacion, Orden, Posicion, Dato, ValorDecimal, ValorBooleano, Calificacion, ListaPalabras, GrupoProducto, Modalidad, TecnicaModalidad, DatoPunto
4
 
5
  # Register your models here.
6
  admin.site.register(CategoriaTecnica)
 
32
  admin.site.register(ValorDecimal)
33
  admin.site.register(ValorBooleano)
34
  admin.site.register(Calificacion)
35
+
36
  admin.site.register(ListaPalabras)
37
  admin.site.register(GrupoProducto)
38
+
39
  admin.site.register(Modalidad)
40
+ admin.site.register(TecnicaModalidad)
41
+
42
+ admin.site.register(DatoPunto)
tecnicas/controllers/api_controller/rating_napping_controller.py CHANGED
@@ -1,12 +1,96 @@
1
  from django.http import JsonResponse
2
  from django.http import HttpRequest
 
 
 
3
 
4
  class RatingNappingController:
5
  @staticmethod
6
- def saveRating(request: HttpRequest, data: list):
7
- print(data)
 
 
8
 
9
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  return JsonResponse({"message": "Datos guardados exitosamente"})
 
11
  except Exception as e:
 
12
  return JsonResponse({"error": "Error al procesar datos"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.http import JsonResponse
2
  from django.http import HttpRequest
3
+ from django.db import transaction
4
+ from tecnicas.models import Calificacion, DatoPunto, Producto, Participacion
5
+
6
 
7
  class RatingNappingController:
8
  @staticmethod
9
+ def saveRatingCoordinates(request: HttpRequest, data: list):
10
+ participation = Participacion.objects.get(
11
+ id=request.session["id_participation"]
12
+ )
13
 
14
  try:
15
+ with transaction.atomic():
16
+ products_map = RatingNappingController.getProductsMap(
17
+ participation.tecnica)
18
+
19
+ existing_ratings_map = RatingNappingController.getExistingRatingsMap(
20
+ participation.tecnica, participation.catador
21
+ )
22
+
23
+ new_ratings = []
24
+ ids_products = products_map.keys()
25
+ for item in data:
26
+ product_id = int(item["idProduct"])
27
+ if product_id not in existing_ratings_map and product_id in ids_products:
28
+ new_ratings.append(
29
+ Calificacion(
30
+ num_repeticion=0,
31
+ id_producto=products_map[product_id],
32
+ id_tecnica=participation.tecnica,
33
+ id_catador=participation.catador,
34
+ )
35
+ )
36
+
37
+ if new_ratings:
38
+ Calificacion.objects.bulk_create(new_ratings)
39
+ existing_ratings_map = RatingNappingController.getExistingRatingsMap(
40
+ participation.tecnica, participation.catador
41
+ )
42
+
43
+ existing_points_map = RatingNappingController.getExistingPointsMap(
44
+ existing_ratings_map.values())
45
+
46
+ points_to_create = []
47
+ points_to_update = []
48
+
49
+ for item in data:
50
+ product_id = int(item["idProduct"])
51
+ rating = existing_ratings_map.get(product_id)
52
+
53
+ if rating:
54
+ if rating.id in existing_points_map:
55
+ point = existing_points_map[rating.id]
56
+ point.x = item["x"]
57
+ point.y = item["y"]
58
+ points_to_update.append(point)
59
+ else:
60
+ points_to_create.append(
61
+ DatoPunto(
62
+ x=item["x"],
63
+ y=item["y"],
64
+ calificacion=rating,
65
+ )
66
+ )
67
+
68
+ if points_to_create:
69
+ DatoPunto.objects.bulk_create(points_to_create)
70
+
71
+ if points_to_update:
72
+ DatoPunto.objects.bulk_update(points_to_update, ['x', 'y'])
73
+
74
  return JsonResponse({"message": "Datos guardados exitosamente"})
75
+
76
  except Exception as e:
77
+ print("ERROR:", e)
78
  return JsonResponse({"error": "Error al procesar datos"})
79
+
80
+ @staticmethod
81
+ def getProductsMap(id_tecnica):
82
+ products_qs = Producto.objects.filter(id_tecnica=id_tecnica)
83
+ return {p.id: p for p in products_qs}
84
+
85
+ @staticmethod
86
+ def getExistingRatingsMap(id_tecnica, id_catador):
87
+ ratings = Calificacion.objects.filter(
88
+ id_tecnica=id_tecnica,
89
+ id_catador=id_catador,
90
+ )
91
+ return {r.id_producto.id: r for r in ratings}
92
+
93
+ @staticmethod
94
+ def getExistingPointsMap(ratings):
95
+ points = DatoPunto.objects.filter(calificacion__in=ratings)
96
+ return {p.calificacion.id: p for p in points}
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_napping_controller.py CHANGED
@@ -1,7 +1,8 @@
1
  from django.http import HttpRequest
2
  from django.shortcuts import redirect, render
3
  from django.urls import reverse
4
- from tecnicas.models import Participacion, Producto, TecnicaModalidad
 
5
  from tecnicas.utils import noValidTechnique
6
  from .general_test_controller import GenetalTestController
7
 
@@ -48,4 +49,26 @@ class TestNappingController(GenetalTestController):
48
  products_in_technique = Producto.objects.filter(id_tecnica=technique)
49
  self.context["products"] = products_in_technique
50
 
 
 
51
  return render(request, self.napping_test, self.context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from django.http import HttpRequest
2
  from django.shortcuts import redirect, render
3
  from django.urls import reverse
4
+ from django.db.models import F
5
+ from tecnicas.models import Participacion, Producto, TecnicaModalidad, DatoPunto, Calificacion
6
  from tecnicas.utils import noValidTechnique
7
  from .general_test_controller import GenetalTestController
8
 
 
49
  products_in_technique = Producto.objects.filter(id_tecnica=technique)
50
  self.context["products"] = products_in_technique
51
 
52
+ self.setCoordinates()
53
+
54
  return render(request, self.napping_test, self.context)
55
+
56
+ def setCoordinates(self):
57
+ technique = self.session.tecnica
58
+
59
+ ratings = Calificacion.objects.filter(
60
+ num_repeticion=0,
61
+ id_tecnica=technique,
62
+ id_catador=self.participation.catador
63
+ )
64
+
65
+ data_points = DatoPunto.objects.filter(
66
+ calificacion__in=ratings
67
+ ).values(
68
+ code= F("calificacion__id_producto__codigoProducto"),
69
+ px= F("x"),
70
+ py= F("y"),
71
+ id_product= F("calificacion__id_producto")
72
+ )
73
+
74
+ self.context["data_points"] = list(data_points)
tecnicas/static/js/test-napping.js CHANGED
@@ -81,14 +81,38 @@ function renderPoint(code, xPx, yPx, xVal, yVal) {
81
  point.appendChild(label);
82
  planeContainer.appendChild(point);
83
 
84
- // Also show a permanent label next to the point if desired,
85
- // or just rely on the hover. For now, let's add a small text label below it.
86
  const textLabel = document.createElement('span');
87
  textLabel.className = 'absolute top-4 left-1/2 transform -translate-x-1/2 text-xs font-bold text-gray-700 pointer-events-none';
88
  textLabel.innerText = code;
89
  point.appendChild(textLabel);
90
  }
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  /*
93
  ////
94
  //////
 
81
  point.appendChild(label);
82
  planeContainer.appendChild(point);
83
 
 
 
84
  const textLabel = document.createElement('span');
85
  textLabel.className = 'absolute top-4 left-1/2 transform -translate-x-1/2 text-xs font-bold text-gray-700 pointer-events-none';
86
  textLabel.innerText = code;
87
  point.appendChild(textLabel);
88
  }
89
 
90
+ function checkPoints() {
91
+ const points = document.querySelectorAll('.data-point');
92
+
93
+ points.forEach(point => {
94
+ const code = point.dataset.code;
95
+
96
+ const xVal = parseFloat(point.dataset.px);
97
+ const yVal = parseFloat(point.dataset.py);
98
+
99
+ const rect = planeContainer.getBoundingClientRect();
100
+
101
+ const px = (xVal / PHYSICAL_WIDTH) * rect.width;
102
+ const py = ((PHYSICAL_HEIGHT - yVal) / PHYSICAL_HEIGHT) * rect.height;
103
+
104
+ placedPoints[code] = {
105
+ x: parseFloat(xVal.toFixed(2)),
106
+ y: parseFloat(yVal.toFixed(2)),
107
+ id: point.dataset.idProduct
108
+ };
109
+
110
+ renderPoint(code, px, py, xVal, yVal);
111
+ });
112
+ }
113
+
114
+ checkPoints();
115
+
116
  /*
117
  ////
118
  //////
tecnicas/templates/tecnicas/forms_tester/test_napping.html CHANGED
@@ -64,10 +64,28 @@
64
  <div class="relative w-full max-w-[800px] aspect-[3/2] bg-white border-2 border-gray-400 shadow-inner cursor-crosshair"
65
  id="napping-plane"
66
  style="background-image: linear-gradient(#e5e7eb 1px, transparent 1px), linear-gradient(90deg, #e5e7eb 1px, transparent 1px); background-size: 20px 20px;">
67
- <!-- Axis Labels -->
68
  <span class="absolute bottom-1 right-2 text-xs text-gray-500 font-bold">60cm (X)</span>
69
  <span class="absolute top-2 left-1 text-xs text-gray-500 font-bold">40cm (Y)</span>
70
  <span class="absolute bottom-1 left-1 text-xs text-gray-500 font-bold">0</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  </div>
72
  </div>
73
 
@@ -128,4 +146,31 @@
128
  {% block extra_js %}
129
  <script src="{% static 'js/actions-form.js' %}"></script>
130
  <script src="{% static 'js/test-napping.js' %}"></script>
131
- {% endblock %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  <div class="relative w-full max-w-[800px] aspect-[3/2] bg-white border-2 border-gray-400 shadow-inner cursor-crosshair"
65
  id="napping-plane"
66
  style="background-image: linear-gradient(#e5e7eb 1px, transparent 1px), linear-gradient(90deg, #e5e7eb 1px, transparent 1px); background-size: 20px 20px;">
67
+
68
  <span class="absolute bottom-1 right-2 text-xs text-gray-500 font-bold">60cm (X)</span>
69
  <span class="absolute top-2 left-1 text-xs text-gray-500 font-bold">40cm (Y)</span>
70
  <span class="absolute bottom-1 left-1 text-xs text-gray-500 font-bold">0</span>
71
+
72
+ {% for point in data_points %}
73
+ <div id="point-{{ point.code }}"
74
+ class="data-point absolute w-4 h-4 bg-red-600 rounded-full transform -translate-x-1/2 -translate-y-1/2 cursor-pointer border-2 border-white shadow-md group"
75
+ data-px="{{ point.px }}" data-py="{{ point.py }}" data-code="{{ point.code }}"
76
+ data-id-product="{{ point.id_product }}">
77
+ <div
78
+ class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded whitespace-nowrap z-10 hidden group-hover:block">
79
+ <strong>{{ point.code }}</strong><br>
80
+ X: {{ point.px }}<br>
81
+ Y: {{ point.py }}
82
+ </div>
83
+ <span
84
+ class="absolute top-4 left-1/2 transform -translate-x-1/2 text-xs font-bold text-gray-700 pointer-events-none">
85
+ {{ point.code }}
86
+ </span>
87
+ </div>
88
+ {% endfor %}
89
  </div>
90
  </div>
91
 
 
146
  {% block extra_js %}
147
  <script src="{% static 'js/actions-form.js' %}"></script>
148
  <script src="{% static 'js/test-napping.js' %}"></script>
149
+ {% endblock %}
150
+
151
+ <div id="point-JOW"
152
+ class="absolute w-4 h-4 bg-red-600 rounded-full transform -translate-x-1/2 -translate-y-1/2 cursor-pointer border-2 border-white shadow-md group"
153
+ style="left: 59px; top: 39px;">
154
+ <div
155
+ class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded whitespace-nowrap z-10 hidden group-hover:block">
156
+ <strong>JOW</strong><br>
157
+ X: 4.4<br>
158
+ Y: 37.1
159
+ </div><span
160
+ class="absolute top-4 left-1/2 transform -translate-x-1/2 text-xs font-bold text-gray-700 pointer-events-none">JOW</span>
161
+ </div>
162
+
163
+ <div id="point-JOW"
164
+ class="absolute w-4 h-4 bg-red-600 rounded-full transform -translate-x-1/2 -translate-y-1/2 cursor-pointer border-2 border-white shadow-md group"
165
+ style="left: 58.93px; top: 685.07px;">
166
+ <div
167
+ class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded whitespace-nowrap z-10 hidden group-hover:block">
168
+ <strong>JOW</strong><br>
169
+ X: 4.4<br>
170
+ Y: 37.1
171
+ </div>
172
+ <span
173
+ class="absolute top-4 left-1/2 transform -translate-x-1/2 text-xs font-bold text-gray-700 pointer-events-none">
174
+ JOW
175
+ </span>
176
+ </div>
tecnicas/views/apis/rating_napping.py CHANGED
@@ -7,7 +7,7 @@ def ratingNappingNoMode(req: HttpRequest):
7
  if req.method == "POST":
8
  try:
9
  data = json.loads(req.body.decode("utf-8"))
10
- response = RatingNappingController.saveRating(
11
  request=req, data=data)
12
  return response
13
  except Exception as e:
 
7
  if req.method == "POST":
8
  try:
9
  data = json.loads(req.body.decode("utf-8"))
10
+ response = RatingNappingController.saveRatingCoordinates(
11
  request=req, data=data)
12
  return response
13
  except Exception as e: