# mi_proyecto/pdf/views.py # dpsk - 2026 import io from pathlib import Path from django.http import HttpResponse from django.template.loader import render_to_string from weasyprint import HTML from arches.app.models.resource import Resource from django.contrib.auth.models import Group from datetime import datetime from zoneinfo import ZoneInfo from functools import reduce from operator import getitem from pypdf import PdfReader, PdfWriter import urllib.parse import requests import base64 import json import ast def static_map_view(geojson_colec, sat_map): geojson_dict = ast.literal_eval(geojson_colec) if sat_map: for geo in geojson_dict['features'] : if geo['geometry'].get('type') == 'Polygon': geo['properties'] = { "stroke": "#00ffff", "stroke-width": 2, "stroke-opacity": 0.8, "fill": "#00ffff", "fill-opacity": 0.3 } elif geo['geometry'].get('type') == 'Point': geo['properties'] = { "marker-color": "#ff0000", "marker-size": "small" } elif geo['geometry'].get('type') == 'LineString': geo['properties'] = { "stroke": "#0000ff", "stroke-width": 2, "stroke-opacity": 0.8 } geojson_string = json.dumps(geojson_dict, separators=(',', ':')) encoded_geojson = urllib.parse.quote(geojson_string) username = "mapbox" if sat_map: style_id = "satellite-streets-v12" # streets-v12 | outdoors-v11 | satellite-streets-v12 else: style_id = "light-v11" width, height = 340, 340 # Procedimiento pendiente para extraer el token desde la configuración del sistema access_token = 'pk.eyJ1IjoianNidXJnb3N2IiwiYSI6ImNsaWhoN3ZtMTBxeHUzZm1xaGM0cGpxOXIifQ.ki3vldVB5MwZMqDSpLSjLg' mapbox_url = ( f"https://api.mapbox.com/styles/v1/{username}/{style_id}/static/" f"geojson({encoded_geojson})/auto/{width}x{height}" f"?access_token={access_token}" ) response = requests.get(mapbox_url) return base64.b64encode(response.content).decode('utf-8') def ajustar_medios(resource_media_rel, medios_list): medios = [] img=["image/bmp", "image/gif", "image/jpg", "image/jpeg", "image/png", "image/tif", "image/tiff"] no_imagen_principal = True try: if isinstance(medios_list, list) : for rel in resource_media_rel['related_resources']: if isinstance(medios_list[0], dict) : medio=next((item for item in medios_list if item['@value'] == rel['displayname']), None) else: medio = None for til in rel['tiles']: for dato in til['data']: if isinstance(til['data'][dato], list): for dat in til['data'][dato]: idx = " - " + str(dat['index'] +1) if len(til['data'][dato])>1 else "" if medio is not None and isinstance(medio, dict): medio_nuevo = {'tipo': val for key, val in medio.items() if "Tipo" in key} else: if dat['type'] in img and no_imagen_principal: medio_nuevo = {'tipo':'Imagen principal'} no_imagen_principal = False else: medio_nuevo = {'tipo':'Medio'} medio_nuevo.update({'@value':rel['displayname']+idx, 'file':dat['name'], 'url': dat['url'], 'formato':dat['type']}) medios.append(medio_nuevo) # Reordenamos para que el primer medio de la lista sea de tipo Imagen principal o al menos del tipo imagen medios.sort(key=lambda item: item.get('formato') not in img) medios.sort(key=lambda item: item.get('tipo') != 'Imagen principal') except: pass return medios def find_key_path(data, target_key, current_path=None): if current_path is None: current_path = [] # Verifica si es diccionario válido if isinstance(data, dict): for key, value in data.items(): # si la clave buscada está en el nivel actual del diccionario. if key == target_key: return current_path + [key] # Profundiza en el rastreo mientras agrega la clave actual a la ruta. path = find_key_path(value, target_key, current_path + [key]) if path: return path # En caso de que la clave esté dentro de una lista elif isinstance(data, list): for index, item in enumerate(data): path = find_key_path(item, target_key, current_path + [index]) if path: return path return None def strdict_to_dict(strdict): """ Representación de texto de un diccionario a formato json """ value = {} try: if isinstance(strdict, str): value = json.dumps(ast.literal_eval(strdict)) except: pass return value def export_resource_pdf(request, resourceid, printubic, satmap, printimgs, printpdfs): resource = Resource.objects.get(pk=resourceid) resource_data = resource.to_json() # Corrección de medios relacionados, se normalizan claves y se adicionan propiedades ruta_medios = find_key_path( resource_data, "Medio de información") if ruta_medios is not None: valor_medios=reduce(lambda dict_layer, key: dict_layer[key], ruta_medios, resource_data) resource_rel=resource.get_related_resources(user=request.user,resourceinstance_graphid='6afc4e9e-727e-4152-8084-309267752650') #resource_data["Información gráfica"]["Medio de información"] = ajustar_medios(resource_rel) reduce(getitem, ruta_medios[:-1], resource_data)[ruta_medios[-1]] = ajustar_medios(resource_rel,valor_medios) # Cambio de datos gis en texto a formato json ruta_gis = find_key_path( resource_data, "Coordenadas / Geometría") if ruta_gis is not None: valor_gis=reduce(lambda dict_layer, key: dict_layer[key], ruta_gis, resource_data)["@value"] reduce(getitem, ruta_gis[:-1], resource_data)[ruta_gis[-1]]["@value"] = strdict_to_dict(valor_gis) # Extracción del identificador de grupo ETA eta_group="" ruta_meta_iden = find_key_path( resource_data, "Meta identificación") if ruta_meta_iden is not None: meta_iden=json.loads(strdict_to_dict(reduce(lambda dict_layer, key: dict_layer[key], ruta_meta_iden, resource_data))) eta_group=str(Group.objects.get(pk=meta_iden["grp_eta"])).upper() # Fecha del informe sprpcb_url=request.build_absolute_uri('/')[:-1] fecha=datetime.now(ZoneInfo("America/La_Paz")) map_img = None if ruta_gis is not None and bool(int(printubic)) : if reduce(getitem, ruta_gis[:-1], resource_data)[ruta_gis[-1]]["@value"] : map_img = static_map_view(reduce(getitem, ruta_gis[:-1], resource_data)[ruta_gis[-1]]["@value"], bool(int(satmap))) resources = [{ 'resourceinstanceid': str(resource.resourceinstanceid), 'graph_id': str(resource.graph_id), 'displayname': resource.displayname, 'resource': resource_data, 'sprpcb_url': sprpcb_url, 'fecha': fecha, 'eta_group': eta_group, 'print_ubic': bool(int(printubic)), 'print_imgs': bool(int(printimgs)), 'print_pdfs': bool(int(printpdfs)), 'map_img': map_img }] context = {'resources': resources} match str(resource.graph_id): case "72420da0-db59-4048-aea2-03b0bc5cdcdf": # Actores context["resources"][0]["grafo"]="Organizaciones y personas" nombre_pdf = f'Actor_{resourceid}' html_string = render_to_string('reports/actor_pdf_report.html', context) case '8ea3bf67-89ce-474c-b3cb-6dd5ee3d6198': # Bienes inmuebles context["resources"][0]["grafo"]="Patrimonio material - Bienes inmuebles" html_string = render_to_string('reports/bim_pdf_report.html', context) nombre_pdf = f'bienes_inmuebles_{resourceid}' case '10b03fec-4d85-11ee-a708-ffcd06c08a5d': # Bienes muebles context["resources"][0]["grafo"]="Patrimonio material - Bienes muebles" html_string = render_to_string('reports/bm_pdf_report.html', context) nombre_pdf = f'bienes_muebles_{resourceid}' case "9b0e22c7-5c56-4be6-aa89-ae2632ba28e8": # Inmaterial context["resources"][0]["grafo"]="Patrimonio Inmaterial" html_string = render_to_string('reports/pi_pdf_report.html', context) nombre_pdf = f'Inmaterial_{resourceid}' gen_pdf_file = HTML(string=html_string, base_url=request.build_absolute_uri("/")).write_pdf() merger = PdfWriter() merger.append(PdfReader(io.BytesIO(gen_pdf_file))) root_path = Path(__file__).resolve().parent.parent if ruta_medios is not None and bool(int(printpdfs)) : for medio in reduce(getitem, ruta_medios[:-1], resource_data)[ruta_medios[-1]] : if medio['formato'] == 'application/pdf' : medio_file = f'{root_path}/uploadedfiles/{medio["file"]}' with open(medio_file,'rb') as pdf_file : merger.append(pdf_file) output_buffer = io.BytesIO() merger.write(output_buffer) merger.close() else: output_buffer = io.BytesIO() merger.write(output_buffer) merger.close() output_buffer.seek(0) response = HttpResponse(output_buffer.read(), content_type='application/pdf') response['Content-Disposition'] = f'attachment; filename="{nombre_pdf}.pdf"' return response