Añadir extensions/functions/ponderar_bim.py

This commit is contained in:
2026-06-12 00:51:42 -04:00
parent 7c5e37c4b0
commit bd68d32262

View File

@@ -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