Spaces:
Sleeping
Sleeping
File size: 14,239 Bytes
7baf813 934cca5 bf9adb3 ea65589 934cca5 7baf813 bf9adb3 934cca5 7baf813 bf9adb3 136cbe4 bf9adb3 a4129be bf9adb3 7baf813 934cca5 bf9adb3 a4129be bf9adb3 934cca5 bf9adb3 934cca5 bf9adb3 934cca5 bf9adb3 ea65589 bf9adb3 934cca5 bf9adb3 934cca5 bf9adb3 934cca5 bf9adb3 934cca5 bf9adb3 934cca5 bf9adb3 934cca5 7baf813 bf9adb3 934cca5 ea65589 934cca5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
from django.http import JsonResponse
from django.http import HttpRequest
from django.db import transaction
from tecnicas.models import Calificacion, DatoPunto, Producto, Participacion, Palabra, GrupoProducto, TecnicaModalidad
from tecnicas.forms import ListWordsForm
class RatingNappingController:
@staticmethod
def saveRatingCoordinates(request: HttpRequest, data: list | dict):
participation = Participacion.objects.get(
id=request.session["id_participation"]
)
name_mod = TecnicaModalidad.objects.get(
tecnica=participation.tecnica
).modalidad.nombre.lower()
# Branch based on modality
if name_mod == 'sorting':
return RatingNappingController.processSortingMode(
data, participation
)
if name_mod in ['sin modalidad', 'perfil ultra flash']:
return RatingNappingController.processNappOrPUF(
data, participation
)
else:
return JsonResponse({"error": "Modalidad no soportada"})
@staticmethod
def processSortingMode(data: dict, participation):
try:
with transaction.atomic():
# Extract products array (always present)
products = data.get("products", [])
if not products:
return JsonResponse({"error": "No se proporcionaron productos"})
existing_ratings_map, products_map = RatingNappingController.savePoints(
isSorting=True, products=products, participation=participation
)
# Process groups if they exist
groups = data.get("groups", {})
if groups:
RatingNappingController.processGroupsForSorting(
products, groups, participation, existing_ratings_map, products_map
)
else:
RatingNappingController.deleteAllGroups(participation)
return JsonResponse({"message": "Datos guardados exitosamente"})
except Exception as e:
print("ERROR:", e)
import traceback
traceback.print_exc()
return JsonResponse({"error": f"Error al procesar datos: {str(e)}"})
@staticmethod
def processNappOrPUF(data: list, participation):
try:
with transaction.atomic():
existing_ratings_map, products_map = RatingNappingController.savePoints(
isSorting=False, products=data, participation=participation
)
RatingNappingController.processWordsForRatings(
data, existing_ratings_map
)
return JsonResponse({"message": "Datos guardados exitosamente"})
except Exception as e:
print("ERROR:", e)
return JsonResponse({"error": "Error al procesar datos"})
@staticmethod
def processGroupsForSorting(products, groups, participation, existing_ratings_map, products_map):
"""Process groups for sorting mode
- Creates/updates GrupoProducto instances
- Ensures products don't belong to multiple groups
- Associates words with groups
"""
# Build mapping of product_id to group_id from products array
product_to_group = {}
for product_item in products:
group_code = product_item.get("group", "")
if group_code: # Only if product has a group assigned
product_id = int(product_item["idProduct"])
if product_id in product_to_group:
raise ValueError(
f"Producto {product_id} pertenece a múltiples grupos")
product_to_group[product_id] = group_code
# Get existing groups for this catador and technique
existing_groups = GrupoProducto.objects.filter(
tecnica=participation.tecnica,
catador=participation.catador
)
# Create a map of existing groups by their product composition
# We'll identify groups by the set of products they contain
existing_groups_map = {}
for group in existing_groups:
product_ids = set(group.productos.values_list('id', flat=True))
key = frozenset(product_ids)
existing_groups_map[key] = group
# Build new groups structure
groups_to_create = []
groups_to_update = []
group_products_map = {} # group_id -> [product_ids]
# Organize products by group
for product_id, group_code in product_to_group.items():
if group_code not in group_products_map:
group_products_map[group_code] = []
group_products_map[group_code].append(product_id)
# Process each group
for group_code, product_ids in group_products_map.items():
product_set = frozenset(product_ids)
# Check if this group already exists
if product_set in existing_groups_map:
group = existing_groups_map[product_set]
groups_to_update.append((group, group_code))
else:
# Create new group
group = GrupoProducto(
tecnica=participation.tecnica,
catador=participation.catador
)
groups_to_create.append((group, product_ids, group_code))
# Create new groups
created_groups = []
for group, product_ids, group_code in groups_to_create:
group.save() # Save first to get ID for M2M
# Add products to group
productos = [products_map[pid]
for pid in product_ids if pid in products_map]
group.productos.set(productos)
created_groups.append((group, group_code))
# Combine created and existing groups for word processing
all_groups_for_words = created_groups + groups_to_update
# Delete groups that no longer exist
current_group_sets = set(frozenset(pids)
for pids in group_products_map.values())
for product_set, group in existing_groups_map.items():
if product_set not in current_group_sets:
group.delete()
# Process words for groups
if groups:
RatingNappingController.processWordsForGroups(
groups, all_groups_for_words
)
@staticmethod
def deleteAllGroups(participation):
GrupoProducto.objects.filter(
tecnica=participation.tecnica,
catador=participation.catador
).delete()
@staticmethod
def processWordsForGroups(groups_data, groups_list):
"""Process and associate words to groups
- Creates words that don't exist
- Associates words to GrupoProducto instances
- Handles concurrency
"""
# Collect all unique words from all groups
all_words = set()
for group_id, words in groups_data.items():
if words:
all_words.update(words)
if not all_words:
# No words to process, just clear existing words from groups
for grupo, _ in groups_list:
grupo.palabras.clear()
return
# Get existing words
existing_words = Palabra.objects.filter(
nombre_palabra__in=all_words
)
existing_words_map = {w.nombre_palabra: w for w in existing_words}
# Create missing words with concurrency handling
word_objects = {}
for word_name in all_words:
if word_name in existing_words_map:
word_objects[word_name] = existing_words_map[word_name]
else:
word_obj, created = Palabra.objects.get_or_create(
nombre_palabra=word_name
)
word_objects[word_name] = word_obj
# Associate words with groups
for grupo, group_id in groups_list:
words = groups_data.get(group_id, [])
if words:
words_to_set = [word_objects[word_name]
for word_name in words if word_name in word_objects]
grupo.palabras.set(words_to_set)
else:
grupo.palabras.clear()
@staticmethod
def savePoints(isSorting: bool, products, participation):
try:
with transaction.atomic():
# Get products map for validation
products_map = RatingNappingController.getProductsMap(
participation.tecnica)
# Get existing ratings map
existing_ratings_map = RatingNappingController.getExistingRatingsMap(
participation.tecnica, participation.catador)
if not isSorting:
validation_result = RatingNappingController.validateWords(
products)
if validation_result is not None:
return validation_result
# Create new ratings for products that don't have them
new_ratings = []
ids_products = products_map.keys()
for item in products:
product_id = int(item["idProduct"])
if product_id not in existing_ratings_map and product_id in ids_products:
new_ratings.append(
Calificacion(
num_repeticion=0,
id_producto=products_map[product_id],
id_tecnica=participation.tecnica,
id_catador=participation.catador,
)
)
if new_ratings:
Calificacion.objects.bulk_create(new_ratings)
existing_ratings_map = RatingNappingController.getExistingRatingsMap(
participation.tecnica, participation.catador)
# Process DatoPunto instances from products array
existing_points_map = RatingNappingController.getExistingPointsMap(
existing_ratings_map.values())
points_to_create = []
points_to_update = []
for item in products:
product_id = int(item["idProduct"])
rating = existing_ratings_map.get(product_id)
if rating:
if rating.id in existing_points_map:
point = existing_points_map[rating.id]
point.x = item["x"]
point.y = item["y"]
points_to_update.append(point)
else:
points_to_create.append(
DatoPunto(
x=item["x"],
y=item["y"],
calificacion=rating,
)
)
if points_to_create:
DatoPunto.objects.bulk_create(points_to_create)
if points_to_update:
DatoPunto.objects.bulk_update(points_to_update, ['x', 'y'])
return (existing_ratings_map, products_map)
except Exception as e:
print(e)
return JsonResponse({"error": "Error al guardar los puntos"})
@staticmethod
def validateWords(data: list):
for item in data:
words = item.get("words", [])
if words:
dic_words = {}
for index, word in enumerate(words, start=1):
dic_words[f"palabra_{index}"] = word
form = ListWordsForm(dic_words, new_words=words)
if not form.is_valid():
errors = []
for field, error_list in form.errors.items():
errors.extend(error_list)
return JsonResponse({"error": f"Error en validación de palabras: {', '.join(errors)}"})
return None
@staticmethod
def processWordsForRatings(data: list, existing_ratings_map: dict):
all_words = set()
for item in data:
words = item.get("words", [])
if words:
all_words.update(words)
if not all_words:
return
existing_words = Palabra.objects.filter(
nombre_palabra__in=all_words
)
existing_words_map = {w.nombre_palabra: w for w in existing_words}
word_objects = {}
for word_name in all_words:
if word_name in existing_words_map:
word_objects[word_name] = existing_words_map[word_name]
else:
word_obj, created = Palabra.objects.get_or_create(
nombre_palabra=word_name
)
word_objects[word_name] = word_obj
for item in data:
words = item.get("words", [])
if words:
product_id = int(item["idProduct"])
rating = existing_ratings_map.get(product_id)
if rating:
words_to_set = [word_objects[word_name]
for word_name in words]
rating.palabras.set(words_to_set)
@staticmethod
def getProductsMap(id_tecnica):
products_qs = Producto.objects.filter(id_tecnica=id_tecnica)
return {p.id: p for p in products_qs}
@staticmethod
def getExistingRatingsMap(id_tecnica, id_catador):
ratings = Calificacion.objects.filter(
id_tecnica=id_tecnica,
id_catador=id_catador,
)
return {r.id_producto.id: r for r in ratings}
@staticmethod
def getExistingPointsMap(ratings):
points = DatoPunto.objects.filter(calificacion__in=ratings)
return {p.calificacion.id: p for p in points}
|