From bd68d322628d5cbd024a5456df5edcce3e693860 Mon Sep 17 00:00:00 2001 From: sysadmin Date: Fri, 12 Jun 2026 00:51:42 -0400 Subject: [PATCH] =?UTF-8?q?A=C3=B1adir=20extensions/functions/ponderar=5Fb?= =?UTF-8?q?im.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/functions/ponderar_bim.py | 365 +++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 extensions/functions/ponderar_bim.py diff --git a/extensions/functions/ponderar_bim.py b/extensions/functions/ponderar_bim.py new file mode 100644 index 0000000..ba0f4dc --- /dev/null +++ b/extensions/functions/ponderar_bim.py @@ -0,0 +1,365 @@ +import uuid +from arches.app.functions.base import BaseFunction +from arches.app.models.system_settings import settings +from arches.app.models import models +from arches.app.models.tile import Tile +from arches.app.models.resource import Resource +from django.db import connection, transaction +import json +import logging +from datetime import datetime + +details = { + 'name': 'Valor patrimonial', + 'type': 'node', + 'description': 'Calcula el valor patrimonial en base a ciertos descriptores y valores', + 'defaultconfig': { + "proteccion": { + "valor_ndeId": "d385a291-6ed0-11ee-b4c9-911631b3a94c", + "interesante_ndeId": "5162187a-f132-11ee-95c6-c93a5afc2c06", + "valor_ndegrpId": "d385a293-6ed0-11ee-b4c9-911631b3a94c", + "valor_tipo": ["val_proteccion"], + "valor_tipo_indice": 0 + }, + "niveles": { + "valor_ndeId": "c50d84f4-cdea-11ef-bee9-419cfc3bf4ec", + "valor_ndegrpId": "9b8c37d8-cdea-11ef-bee9-419cfc3bf4ec", + "valor_tipo": ['Urbana','Formal','Tipológico','Integridad'], + "valor_tipo_indice": 2 + }, + "otros_patrimonio": { + "valor_ndeId": "5d841086-7c2f-11ee-b432-e9e27a5b849b", + "valor_ndegrpId": "5d841086-7c2f-11ee-b432-e9e27a5b849b", + "valor_tipo": ["Inmaterial","Formal","Muebles","Natural"], + "valor_tipo_indice": 3 + }, + "seccion_exterior": { + "valor_ndeId": "0a0451d4-cdee-11ef-bee9-419cfc3bf4ec", + "valor_ndegrpId": "f333aeab-7849-11ee-9872-197bdd009cf7", + "valor_tipo": ['Artística','Arquitectónica','Tipológico','Integridad'], + "valor_tipo_indice": 4 + }, + "seccion_interior": { + "valor_ndeId": "7349a5b8-cdee-11ef-bee9-419cfc3bf4ec", + "valor_ndegrpId": "d98ea571-7c15-11ee-9f9d-13533b0eb7ec", + "valor_tipo": ['Artística','Arquitectónica','Tipológico'], + "valor_tipo_indice": 5 + }, + "circulacion": { + "valor_ndeId": "adf46dba-cdee-11ef-bee9-419cfc3bf4ec", + "valor_ndegrpId": "1d6bad39-7c1b-11ee-9f9d-13533b0eb7ec", + "valor_tipo": ['Artística','Tipológico'], + "valor_tipo_indice": 6 + }, + "espacios_exteriores": { + "valor_ndeId": "e14ac826-cdee-11ef-bee9-419cfc3bf4ec", + "valor_ndegrpId": "a1871e49-7c1d-11ee-9f9d-13533b0eb7ec", + "valor_tipo": ['Artística',], + "valor_tipo_indice": 7 + }, + "valorizacion": { + "valor_ndeId": "c72dae37-72c7-11ee-b797-2be08bcc2bc6", + "valor_ndegrpId": "c72dae37-72c7-11ee-b797-2be08bcc2bc6", + "valor_tipo": ['Urbana','Individual','De conjunto','Arquitectónica','Formal','Tipológico','Histórica','Artística','Integridad','Inmaterial','Simbólico','Natural','Muebles'], + "valor_tipo_indice": 1 + }, + "valor_string_map": "{'val_final': 'Sin valor', 'rango':[1,25,50], 'val_total':0, 'val_proteccion': [0,0,0,0,0,0],"\ + "'Urbana': [0,0,0,0,0,0,0,0], 'Individual': [0,0,0,0,0,0,0,0],'De conjunto': [0,0,0,0,0,0,0,0],"\ + "'Arquitectónica': [0,0,0,0,0,0,0,0], 'Formal': [0,0,0,0,0,0,0,0], 'Tipológico': [0,0,0,0,0,0,0,0],"\ + "'Histórica': [0,0,0,0,0,0,0,0], 'Artística': [0,0,0,0,0,0,0,0], 'Integridad': [0,0,0,0,0,0,0,0],"\ + "'Inmaterial': [0,0,0,0,0,0,0,0], 'Simbólico': [0,0,0,0,0,0,0,0], 'Natural': [0,0,0,0,0,0,0,0],"\ + "'Muebles': [0,0,0,0,0,0,0,0]}", + "valor_total_id": "1d5b879a-cdec-11ef-bee9-419cfc3bf4ec", # Nodo para valor numérico calculado. Ej. 38 + "valor_final_id": "0511276c-cdec-11ef-bee9-419cfc3bf4ec", # Nodo para valor literal por categoría. Ej. Valor B + "rango_tipo_id": "c2974c7c-cdeb-11ef-bee9-419cfc3bf4ec", # Nodo para el rango, literal. Ej. [1,25,50] + "factor_ajuste_id":"da4a038c-cdeb-11ef-bee9-419cfc3bf4ec", # Nodo para introducir un valor numérico de ajuste. + "valor_tipo_id": "8747962c-cdeb-11ef-bee9-419cfc3bf4ec", # Nodo para valor_string_map, literal + "triggering_nodegroups": [] + }, + "classname": "ValoracionPatrimonial", + "component": "", + "functionid":"d206bc88-1c17-4860-b70c-5db9af5ab603", +} + +class ValoracionPatrimonial(BaseFunction): + def get(self): + raise NotImplementedError + + def valor_nodo_ponderable(self, tile, request, is_function_save_method=True): + """ + Función que calcula el valor patrimonial en base a descriptores específicos + + Args: + self: Objeto código. + tile: Tile al que se adjuntará/modificará el código. + request: Solicitud WSGI para verificar que la llamada sea acción del usuario. La función finaliza en valor nulo. + is_function_save_method: Un valor booleano que indica si la función que lo llama es la función de guardado. + + jbti - 10.4.25 + """ + if request is None and is_function_save_method == True: + return + + valorDictMap={} + conceptoValorSumar = {'00000000-0000-0000-7585-000000000000': [0, 'ElementoSinValor']} # estructura (ej.): {'id1':[2,'Urbano'],'id2':....} + + def format_string_value(id_string_value): + languages = models.Language.objects.all() + default_language = languages.get(code=settings.LANGUAGE_CODE) + return {default_language.code: {"value": id_string_value, "direction": default_language.default_direction}} + + def limpiar_valores(preValorDictMap,indiceVal): + for val in preValorDictMap: + match val: + case 'val_final' | 'val_total' | 'rango' | 'val_proteccion': + pass + case _: + preValorDictMap[val][indiceVal] = 0 + return preValorDictMap + + def calcular_valores_parciales(preValorDictMap): + sumaParcial = 0 + promElementos = 0 + for val in preValorDictMap: + match val: + case 'val_final' | 'val_total' | 'rango': + sumaTotal = 0 + case 'val_proteccion': + sumaTotal = preValorDictMap[val][0] + numSecciones = preValorDictMap[val][2]+preValorDictMap[val][3]+preValorDictMap[val][4]+preValorDictMap[val][5] + case _: + sumaParcial = sumaParcial + preValorDictMap[val][1]+preValorDictMap[val][2]+preValorDictMap[val][3] + promElementos = promElementos + preValorDictMap[val][4]+preValorDictMap[val][5]+preValorDictMap[val][6]+preValorDictMap[val][7] + sumaTotal = sumaTotal + sumaParcial + if numSecciones in (1,2,3,4): + sumaTotal = sumaTotal + promElementos//4 #numSecciones + preValorDictMap['val_total'] = sumaTotal + + match sumaTotal: + case sumaTotal if sumaTotal < preValorDictMap['rango'][0]: + valorFinalRango ="Sin valor" + case sumaTotal if (sumaTotal >= preValorDictMap['rango'][0] and sumaTotal < preValorDictMap['rango'][1]): + valorFinalRango = "Valor C" + case sumaTotal if (sumaTotal >= preValorDictMap['rango'][1] and sumaTotal < preValorDictMap['rango'][2]): + valorFinalRango ="Valor B" + case sumaTotal if sumaTotal >= preValorDictMap['rango'][2]: + valorFinalRango = "Valor A" + case _: + valorFinalRango = "Valor desconocido" + + if preValorDictMap['val_proteccion'][1] == 0: valorFinalRango = "Sin valor" + preValorDictMap['val_final'] = valorFinalRango + return preValorDictMap + + def escribir_valores(tileValFinal,preValorDictMap): + tileValFinal.data[self.config['valor_total_id'] ] = int(preValorDictMap['val_total']) + tileValFinal.data[self.config['valor_final_id'] ] = format_string_value(str(preValorDictMap['val_final'])) + tileValFinal.data[self.config['rango_tipo_id']] = format_string_value(str(preValorDictMap['rango'])) + tileValFinal.data[self.config['valor_tipo_id']] = format_string_value(str(preValorDictMap)) + if str(tile.nodegroup_id) != self.config['valorizacion']['valor_ndegrpId'] : + tileValFinal.save() + + return + + def extraer_concepto_valor(string_id_concepto,str_tipo_valor): + if string_id_concepto is not None: + qry = f""" + SELECT v.value + FROM values v INNER JOIN values vv ON v.conceptid = vv.conceptid + WHERE v.valuetype='{str_tipo_valor}' AND vv.valueid='{string_id_concepto}' + """ + cursor = connection.cursor() + cursor.execute(qry) + valorConcepto = cursor.fetchone()[0] + if valorConcepto is None: valorConcepto = 0 + cursor.close() + else: + valorConcepto = 0 + return valorConcepto + + def ponderar_elem_const(resourceinstanceID, fichaValorMap, idNGrpExterior, + idNodoCambio, valoresDistribuidos, indiceValorMap): + sumarValor = 0 + nivelesElemconst = set() + + elemTiles = Tile.objects.filter(nodegroup_id=idNGrpExterior,resourceinstance_id=resourceinstanceID) + def calculo_por_tile(valTile): + elemValor = 0 + cambioVal = 0 + if idNGrpExterior in valTile.data and valTile.data[idNGrpExterior] is not None: + if len(valTile.data[idNGrpExterior]) > 0 : + elemValorId = valTile.data[idNGrpExterior][0] + else: + elemValorId = '00000000-0000-0000-7585-000000000000' + if elemValorId not in conceptoValorSumar : + conceptoValorSumar.update({elemValorId : [int(extraer_concepto_valor(elemValorId,'hiddenLabel')), str(extraer_concepto_valor(elemValorId,'prefLabel'))]}) + elemValor = conceptoValorSumar[elemValorId][0] + if idNodoCambio in valTile.data and valTile.data[idNodoCambio] is not None: + cambioValId = valTile.data[idNodoCambio] + if cambioValId not in conceptoValorSumar: + conceptoValorSumar.update({cambioValId : [int(extraer_concepto_valor(cambioValId,'hiddenLabel')), str(extraer_concepto_valor(cambioValId,'prefLabel'))]}) + cambioVal = conceptoValorSumar[cambioValId][0] + return elemValor * cambioVal + + if len(elemTiles) > 0 : + for otroTile in elemTiles: + nivelesElemconst.add(otroTile.parenttile.tileid) + if str(otroTile.tileid) != str(tile.tileid) : + nivelesElemconst.add(otroTile.parenttile.tileid) + sumarValor = sumarValor + calculo_por_tile(otroTile) + + sumarValor = sumarValor + calculo_por_tile(tile) + if len(nivelesElemconst) > 0 : sumarValor = sumarValor // len(nivelesElemconst) + else: + sumarValor = calculo_por_tile(tile) + if tile is not None: + fichaValorMap['val_proteccion'][indiceValorMap - 2] = 1 + else: + fichaValorMap['val_proteccion'][indiceValorMap - 2] = 0 + + limpiar_valores(fichaValorMap,indiceValorMap) + for valorDistribuido in valoresDistribuidos: + if fichaValorMap['val_proteccion'][indiceValorMap -2] == 1: + fichaValorMap[valorDistribuido][indiceValorMap] = sumarValor + + return fichaValorMap + + def ponderar_proteccion(proteccionTile,idNodoEsDeInteres,fichaValorMap,idNodoProteccion,indiceVal): + + if idNodoEsDeInteres in proteccionTile.data : + match proteccionTile.data[idNodoEsDeInteres] : + case True: + fichaValorMap['val_proteccion'][1] = 1 + case _: # False or None + fichaValorMap['val_proteccion'][1] = 0 + sumaValoresProteccion = 0 + if idNodoProteccion in proteccionTile.data and proteccionTile.data[idNodoProteccion] is not None: + listaValoresId = proteccionTile.data[idNodoProteccion] + for valId in listaValoresId: + if valId not in conceptoValorSumar: + conceptoValorSumar.update({valId : [int(extraer_concepto_valor(valId,'hiddenLabel')), str(extraer_concepto_valor(valId,'prefLabel'))]}) + sumaValoresProteccion = sumaValoresProteccion + conceptoValorSumar[valId][0] + fichaValorMap['val_proteccion'][indiceVal] = sumaValoresProteccion + + return fichaValorMap + + def ponderar_niveles(nivelesTile,idNodoValorNiveles,fichaValorMap,valorDistribuido,indiceVal): + if idNodoValorNiveles in nivelesTile.data : #and nivelesTile[0].data[idNodoValorNiveles] is not None: + valorId = nivelesTile.data[idNodoValorNiveles] + if valorId not in conceptoValorSumar: + conceptoValorSumar.update({valorId : [int(extraer_concepto_valor(valorId,'hiddenLabel')), str(extraer_concepto_valor(valorId,'prefLabel'))]}) + for valDistribuido in valorDistribuido: + fichaValorMap[valDistribuido][indiceVal] = conceptoValorSumar[valorId][0] + return fichaValorMap + + def ponderar_otros(otrosTile,idNGrpValorOtros,fichaValorMap,indiceVal): + if idNGrpValorOtros in otrosTile.data and otrosTile.data[idNGrpValorOtros] is not None: + listaValoresId = otrosTile.data[idNGrpValorOtros] + for valId in listaValoresId : + if valId not in conceptoValorSumar : + conceptoValorSumar.update({valId : [int(extraer_concepto_valor(valId,'hiddenLabel')), str(extraer_concepto_valor(valId,'prefLabel'))]}) + fichaValorMap[conceptoValorSumar[valId][1]][indiceVal] = conceptoValorSumar[valId][0] + return fichaValorMap + + def ponderar_valorizacion(valorizacionTile,idNGrpValorizacion,fichaValorMap,indiceVal): + if idNGrpValorizacion in valorizacionTile.data and valorizacionTile.data[idNGrpValorizacion] is not None: + listaValoresId = valorizacionTile.data[idNGrpValorizacion] + for valId in listaValoresId : + if valId not in conceptoValorSumar : + conceptoValorSumar.update({valId : [int(extraer_concepto_valor(valId,'hiddenLabel')), str(extraer_concepto_valor(valId,'prefLabel'))]}) + if conceptoValorSumar[valId][1] != 'Sin valor patrimonial' : + fichaValorMap[conceptoValorSumar[valId][1]][indiceVal] = conceptoValorSumar[valId][0] + return fichaValorMap + + try: + + """Se carga el valor final (valorStrinMap) o se pone un valor predeterminado (0)""" + if str(tile.nodegroup_id) != str(self.config['valorizacion']['valor_ndegrpId']): + vfinalTiles = Tile.objects.filter(nodegroup_id=str(self.config['valorizacion']['valor_ndegrpId']), + resourceinstance_id=tile.resourceinstance_id) + if len(vfinalTiles) > 0 : + # (Falta) Procedimiento para validar el valor final existente + vfinalTile = vfinalTiles[0] + if vfinalTile.data[self.config['valor_tipo_id']] is not None: + valorDictMap = eval(str(vfinalTile.data[self.config['valor_tipo_id']]['es']['value'])) + if len(valorDictMap['val_proteccion']) == 2 : valorDictMap = eval(str(self.config['valor_string_map'])) # Reinicio + else: + valorDictMap = eval(str(self.config['valor_string_map'])) + else: + valorDictMap = eval(str(self.config['valor_string_map'])) + tilesCarac = Tile.objects.filter(nodegroup_id = 'c72dae1f-72c7-11ee-b797-2be08bcc2bc6', + resourceinstance_id = tile.resourceinstance_id) + if len(tilesCarac) == 0 : + tileCarac = Tile.get_blank_tile_from_nodegroup_id('c72dae1f-72c7-11ee-b797-2be08bcc2bc6', tile.resourceinstance_id, None) + tileCarac.save() + else: + tileCarac = tilesCarac[0] + vfinalTile = Tile.get_blank_tile_from_nodegroup_id(self.config['valorizacion']['valor_ndegrpId'], + tile.resourceinstance_id, tileCarac) + else: + vfinalTile = tile + if vfinalTile.data[self.config['valor_tipo_id']] is not None: + valorDictMap = eval(str(vfinalTile.data[self.config['valor_tipo_id']]['es']['value'])) + if len(valorDictMap['val_proteccion']) == 2 : valorDictMap = eval(str(self.config['valor_string_map'])) + else: + valorDictMap = eval(str(self.config['valor_string_map'])) + + """ Pondera los valores según el nodo actualmente editado """ + if tile.nodegroup_id == str(self.config['proteccion']['valor_ndegrpId']) : + valorDictMap = ponderar_proteccion(tile, self.config['proteccion']['interesante_ndeId'], valorDictMap, + self.config['proteccion']['valor_ndeId'], self.config['proteccion']['valor_tipo_indice']) + elif tile.nodegroup_id == str(self.config['niveles']['valor_ndegrpId']) : + valorDictMap = ponderar_niveles(tile, self.config['niveles']['valor_ndeId'], valorDictMap, + self.config['niveles']['valor_tipo'], self.config['niveles']['valor_tipo_indice']) + elif tile.nodegroup_id == str(self.config['otros_patrimonio']['valor_ndegrpId']) : + valorDictMap = limpiar_valores(valorDictMap,self.config['otros_patrimonio']['valor_tipo_indice']) + valorDictMap = ponderar_otros(tile, self.config['otros_patrimonio']['valor_ndeId'], valorDictMap, + self.config['otros_patrimonio']['valor_tipo_indice']) + elif tile.nodegroup_id == str(self.config['valorizacion']['valor_ndegrpId']) : + valorDictMap = limpiar_valores(valorDictMap,self.config['valorizacion']['valor_tipo_indice']) + valorDictMap = ponderar_valorizacion(tile, self.config['valorizacion']['valor_ndeId'], valorDictMap, + self.config['valorizacion']['valor_tipo_indice']) + elif tile.nodegroup_id == str(self.config['seccion_exterior']['valor_ndegrpId']) : + valorDictMap = ponderar_elem_const(tile.resourceinstance_id, valorDictMap, self.config['seccion_exterior']['valor_ndegrpId'], + self.config['seccion_exterior']['valor_ndeId'], self.config['seccion_exterior']['valor_tipo'], + self.config['seccion_exterior']['valor_tipo_indice']) + elif tile.nodegroup_id == str(self.config['seccion_interior']['valor_ndegrpId']) : + valorDictMap = ponderar_elem_const(tile.resourceinstance_id, valorDictMap, self.config['seccion_interior']['valor_ndegrpId'], + self.config['seccion_interior']['valor_ndeId'], self.config['seccion_interior']['valor_tipo'], + self.config['seccion_interior']['valor_tipo_indice']) + elif tile.nodegroup_id == str(self.config['circulacion']['valor_ndegrpId']) : + valorDictMap = ponderar_elem_const(tile.resourceinstance_id, valorDictMap, self.config['circulacion']['valor_ndegrpId'], + self.config['circulacion']['valor_ndeId'], self.config['circulacion']['valor_tipo'], + self.config['circulacion']['valor_tipo_indice']) + elif tile.nodegroup_id == str(self.config['espacios_exteriores']['valor_ndegrpId']) : + valorDictMap = ponderar_elem_const(tile.resourceinstance_id, valorDictMap, self.config['espacios_exteriores']['valor_ndegrpId'], + self.config['espacios_exteriores']['valor_ndeId'], self.config['espacios_exteriores']['valor_tipo'], + self.config['espacios_exteriores']['valor_tipo_indice']) + + valorDictMap = calcular_valores_parciales(valorDictMap) + + escribir_valores(vfinalTile, valorDictMap) + + return + + except Exception as e: + self.logger.error(e,"\t*** Fallo al actualizar la ponderación") + return + + def save(self, tile, request, context=None): + self.logger = logging.getLogger(__name__) + print("\n\t*** llamado a la función ponderar.save ***") + self.valor_nodo_ponderable(tile=tile, request=request, is_function_save_method=True) + return + + def post_save(self, *args, **kwargs): + raise NotImplementedError + + def delete(self, tile, request): + raise NotImplementedError + + def on_import(self, tile): + raise NotImplementedError + + def after_function_save(self, tile, request): + raise NotImplementedError \ No newline at end of file