assemblerpdf 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- assemblerpdf-1.0.0/MANIFEST.in +4 -0
- assemblerpdf-1.0.0/PKG-INFO +86 -0
- assemblerpdf-1.0.0/README.md +63 -0
- assemblerpdf-1.0.0/pyproject.toml +47 -0
- assemblerpdf-1.0.0/setup.cfg +4 -0
- assemblerpdf-1.0.0/src/assemblerpdf/__init__.py +16 -0
- assemblerpdf-1.0.0/src/assemblerpdf/assemblers/__init__.py +9 -0
- assemblerpdf-1.0.0/src/assemblerpdf/assemblers/base.py +120 -0
- assemblerpdf-1.0.0/src/assemblerpdf/assemblers/form.py +257 -0
- assemblerpdf-1.0.0/src/assemblerpdf/assemblers/report.py +44 -0
- assemblerpdf-1.0.0/src/assemblerpdf/core.py +193 -0
- assemblerpdf-1.0.0/src/assemblerpdf/fonts/Inter-Bold.ttf +0 -0
- assemblerpdf-1.0.0/src/assemblerpdf/fonts/Inter-Medium.ttf +0 -0
- assemblerpdf-1.0.0/src/assemblerpdf/fonts/Inter-Regular.ttf +0 -0
- assemblerpdf-1.0.0/src/assemblerpdf/img/Logo-unp-rojo.png +0 -0
- assemblerpdf-1.0.0/src/assemblerpdf/img/logo-mininterior-rojo.jpg +0 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/checkbox.html +16 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/footer.html +16 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/grid_row.html +7 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/header.html +19 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/input.html +12 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/radio.html +14 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/section.html +6 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/signature.html +14 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/style.html +318 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/assemblerpdf/textarea.html +12 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/anexos.html +38 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/anexos_limpio.html +52 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/archivese.html +18 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/autoriza_envio.html +33 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/checkbox.html +41 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/control_cambios.html +43 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/datos_basicosinf.html +16 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/datos_ot.html +33 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/encaso_de.html +5 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fiila_dos_check.html +54 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_cinco_zona.html +54 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_cuatro.html +20 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_cuatro_si_no.html +46 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_cuatro_zona.html +48 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_dos.html +12 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_dos_zona.html +40 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_instructivo.html +16 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_tres.html +16 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_tres_checkbox.html +66 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_tres_zona.html +45 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/fila_uno.html +8 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/firma.html +10 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/firma_doble.html +32 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/firmaop2.html +17 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/firmas_oficializacion.html +66 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/hechos_sobrevinientes.html +261 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/hechos_sobrevinientes_vacio.html +209 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/iformfilacuatro.html +20 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/informacion_doble.html +0 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/input_2checks.html +98 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/input_check.html +40 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/input_checkbox.html +53 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/input_solitario.html +9 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/label_input_dos.html +24 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/leyenda_autoriza_correo.html +8 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/leyenda_superiol.html +5 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/logos.html +4 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/manifiesto.html +41 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/notificacion_correo.html +14 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/oficializacion.html +21 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/pregunta_si_no.html +34 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/pregunta_tresopciones.html +47 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/qr_oficializacion.html +7 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/situacion_amenaza.html +82 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/style.html +228 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/subtitulo_icono.html +6 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/subtitulo_icono_largo.html +6 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/tabla_doble.html +15 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/tabla_instructivo.html +12 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/tabla_matriz_riesgo.html +135 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/tabla_proyeccion.html +37 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/tabla_simple.html +187 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/texarea.html +9 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/tipo_identificacion.html +55 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/tipos_checkbox.html +47 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/titulo.html +6 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/titulo_logo.html +22 -0
- assemblerpdf-1.0.0/src/assemblerpdf/templates/sistema/titulo_principal.html +5 -0
- assemblerpdf-1.0.0/src/assemblerpdf/utils/__init__.py +1 -0
- assemblerpdf-1.0.0/src/assemblerpdf/utils/template_loader.py +48 -0
- assemblerpdf-1.0.0/src/assemblerpdf.egg-info/PKG-INFO +86 -0
- assemblerpdf-1.0.0/src/assemblerpdf.egg-info/SOURCES.txt +89 -0
- assemblerpdf-1.0.0/src/assemblerpdf.egg-info/dependency_links.txt +1 -0
- assemblerpdf-1.0.0/src/assemblerpdf.egg-info/requires.txt +4 -0
- assemblerpdf-1.0.0/src/assemblerpdf.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: assemblerpdf
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Generador de PDFs usando WeasyPrint, diseñado para formularios y reportes con componentes genéricos.
|
|
5
|
+
Author-email: EcosistemaUNP <ecosistema@unp.gov.co>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: django>=3.2
|
|
20
|
+
Requires-Dist: weasyprint>=60.0
|
|
21
|
+
Requires-Dist: jinja2>=3.0.0
|
|
22
|
+
Requires-Dist: requests>=2.25.0
|
|
23
|
+
|
|
24
|
+
# 🚀 assemblerpdf
|
|
25
|
+
|
|
26
|
+
> ✨ **Generador de PDFs dinámicos utilizando WeasyPrint con componentes HTML genéricos y reutilizables.**
|
|
27
|
+
|
|
28
|
+
Esta librería permite ensamblar PDFs complejos utilizando un conjunto de componentes reutilizables (`grid_row`, `input`, `textarea`, `checkbox`, `radio`, etc.), con soporte para la fuente **Inter** descargada automáticamente, y con instalador automatizado de dependencias del sistema operativo (Pango, Cairo, etc.).
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## ⚡ Instalación y Configuración
|
|
33
|
+
|
|
34
|
+
Puedes instalar la librería localmente para desarrollo:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install -e .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
E instalar las dependencias del sistema (como Cairo/Pango para WeasyPrint) y descargar las fuentes corriendo el siguiente comando en Python:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import assemblerpdf
|
|
44
|
+
assemblerpdf.install_dependencies()
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 🛠️ Uso Básico
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from assemblerpdf.assemblers.base import BaseFormAssembler
|
|
53
|
+
|
|
54
|
+
# Crear contexto de datos
|
|
55
|
+
context = {
|
|
56
|
+
"nombreCompleto": "Juan Pérez",
|
|
57
|
+
"fechaDiligenciamiento": "2026-06-03"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Inicializar ensamblador
|
|
61
|
+
assembler = BaseFormAssembler(context)
|
|
62
|
+
|
|
63
|
+
# Agregar componentes genéricos
|
|
64
|
+
assembler.add_component("header.html", {
|
|
65
|
+
"titulo1": "FORMULARIO DE PRUEBA",
|
|
66
|
+
"titulo2": "SUBTÍTULO",
|
|
67
|
+
"titulo3": "UNIDAD NACIONAL DE PROTECCIÓN"
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
assembler.add_component("section.html", {
|
|
71
|
+
"titulo": "Datos de Identificación",
|
|
72
|
+
"icono": "fa-user"
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
# Agregar fila de inputs
|
|
76
|
+
assembler.add_grid_row([
|
|
77
|
+
{"template": "input.html", "context": {"label": "Nombre Completo", "valor": context["nombreCompleto"], "ancho": "70%"}},
|
|
78
|
+
{"template": "input.html", "context": {"label": "Fecha", "valor": context["fechaDiligenciamiento"], "ancho": "30%"}}
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
# Construir PDF
|
|
82
|
+
pdf_bytes = assembler.build()
|
|
83
|
+
|
|
84
|
+
with open("test.pdf", "wb") as f:
|
|
85
|
+
f.write(pdf_bytes)
|
|
86
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 🚀 assemblerpdf
|
|
2
|
+
|
|
3
|
+
> ✨ **Generador de PDFs dinámicos utilizando WeasyPrint con componentes HTML genéricos y reutilizables.**
|
|
4
|
+
|
|
5
|
+
Esta librería permite ensamblar PDFs complejos utilizando un conjunto de componentes reutilizables (`grid_row`, `input`, `textarea`, `checkbox`, `radio`, etc.), con soporte para la fuente **Inter** descargada automáticamente, y con instalador automatizado de dependencias del sistema operativo (Pango, Cairo, etc.).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ⚡ Instalación y Configuración
|
|
10
|
+
|
|
11
|
+
Puedes instalar la librería localmente para desarrollo:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install -e .
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
E instalar las dependencias del sistema (como Cairo/Pango para WeasyPrint) y descargar las fuentes corriendo el siguiente comando en Python:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import assemblerpdf
|
|
21
|
+
assemblerpdf.install_dependencies()
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🛠️ Uso Básico
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from assemblerpdf.assemblers.base import BaseFormAssembler
|
|
30
|
+
|
|
31
|
+
# Crear contexto de datos
|
|
32
|
+
context = {
|
|
33
|
+
"nombreCompleto": "Juan Pérez",
|
|
34
|
+
"fechaDiligenciamiento": "2026-06-03"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Inicializar ensamblador
|
|
38
|
+
assembler = BaseFormAssembler(context)
|
|
39
|
+
|
|
40
|
+
# Agregar componentes genéricos
|
|
41
|
+
assembler.add_component("header.html", {
|
|
42
|
+
"titulo1": "FORMULARIO DE PRUEBA",
|
|
43
|
+
"titulo2": "SUBTÍTULO",
|
|
44
|
+
"titulo3": "UNIDAD NACIONAL DE PROTECCIÓN"
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
assembler.add_component("section.html", {
|
|
48
|
+
"titulo": "Datos de Identificación",
|
|
49
|
+
"icono": "fa-user"
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
# Agregar fila de inputs
|
|
53
|
+
assembler.add_grid_row([
|
|
54
|
+
{"template": "input.html", "context": {"label": "Nombre Completo", "valor": context["nombreCompleto"], "ancho": "70%"}},
|
|
55
|
+
{"template": "input.html", "context": {"label": "Fecha", "valor": context["fechaDiligenciamiento"], "ancho": "30%"}}
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
# Construir PDF
|
|
59
|
+
pdf_bytes = assembler.build()
|
|
60
|
+
|
|
61
|
+
with open("test.pdf", "wb") as f:
|
|
62
|
+
f.write(pdf_bytes)
|
|
63
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "assemblerpdf"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Generador de PDFs usando WeasyPrint, diseñado para formularios y reportes con componentes genéricos."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "EcosistemaUNP", email = "ecosistema@unp.gov.co"},
|
|
12
|
+
]
|
|
13
|
+
license = {text = "MIT"}
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
]
|
|
26
|
+
requires-python = ">=3.8"
|
|
27
|
+
dependencies = [
|
|
28
|
+
"django>=3.2",
|
|
29
|
+
"weasyprint>=60.0",
|
|
30
|
+
"jinja2>=3.0.0",
|
|
31
|
+
"requests>=2.25.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools]
|
|
35
|
+
package-dir = {"" = "src"}
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.package-data]
|
|
41
|
+
assemblerpdf = [
|
|
42
|
+
"templates/assemblerpdf/*.html",
|
|
43
|
+
"templates/sistema/*.html",
|
|
44
|
+
"fonts/*.ttf",
|
|
45
|
+
"img/*.png",
|
|
46
|
+
"img/*.jpg"
|
|
47
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .core import (
|
|
2
|
+
generate_pdf,
|
|
3
|
+
generate_pdf_from_string,
|
|
4
|
+
install_dependencies,
|
|
5
|
+
download_inter_font,
|
|
6
|
+
check_weasyprint_installed
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__version__ = "1.0.0"
|
|
10
|
+
__all__ = [
|
|
11
|
+
"generate_pdf",
|
|
12
|
+
"generate_pdf_from_string",
|
|
13
|
+
"install_dependencies",
|
|
14
|
+
"download_inter_font",
|
|
15
|
+
"check_weasyprint_installed",
|
|
16
|
+
]
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from assemblerpdf.core import generate_pdf_from_string
|
|
3
|
+
|
|
4
|
+
class BaseFormAssembler:
|
|
5
|
+
def __init__(self, context=None):
|
|
6
|
+
self.context = context or {}
|
|
7
|
+
self.components = []
|
|
8
|
+
|
|
9
|
+
def _get_img_base(self):
|
|
10
|
+
import assemblerpdf
|
|
11
|
+
package_path = os.path.dirname(assemblerpdf.__file__)
|
|
12
|
+
img_dir = os.path.join(package_path, 'img')
|
|
13
|
+
img_dir = img_dir.replace('\\', '/')
|
|
14
|
+
return f'file:///{img_dir}'
|
|
15
|
+
|
|
16
|
+
def _render(self, template_name, extra_context=None):
|
|
17
|
+
"""Renderiza un componente usando Django (si está disponible) o Jinja2."""
|
|
18
|
+
# Unificar contexto
|
|
19
|
+
full_context = {
|
|
20
|
+
'img_base': self._get_img_base(),
|
|
21
|
+
**self.context,
|
|
22
|
+
**(extra_context or {})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Intentar usar Django primero si está configurado
|
|
26
|
+
try:
|
|
27
|
+
from django.template.loader import render_to_string
|
|
28
|
+
# Asegurar prefijo de carpeta
|
|
29
|
+
if not template_name.startswith("assemblerpdf/"):
|
|
30
|
+
django_path = f"assemblerpdf/{template_name}"
|
|
31
|
+
else:
|
|
32
|
+
django_path = template_name
|
|
33
|
+
return render_to_string(django_path, full_context)
|
|
34
|
+
except Exception:
|
|
35
|
+
# Fallback a Jinja2
|
|
36
|
+
from assemblerpdf.utils.template_loader import render_template
|
|
37
|
+
return render_template(template_name, full_context)
|
|
38
|
+
|
|
39
|
+
def add_component(self, template_name, extra_context=None, wrapper_html=None):
|
|
40
|
+
"""
|
|
41
|
+
Agrega un componente individual al ensamblador.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
template_name (str): Nombre del template (ej: 'input.html')
|
|
45
|
+
extra_context (dict): Contexto adicional para el template
|
|
46
|
+
wrapper_html (str): HTML envolvente opcional
|
|
47
|
+
"""
|
|
48
|
+
rendered = self._render(template_name, extra_context)
|
|
49
|
+
|
|
50
|
+
if wrapper_html:
|
|
51
|
+
rendered = wrapper_html.replace('{{component}}', rendered)
|
|
52
|
+
|
|
53
|
+
self.components.append(rendered)
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def add_grid_row(self, fields):
|
|
57
|
+
"""
|
|
58
|
+
Agrega una fila (grid_row) con múltiples campos alineados horizontalmente.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
fields (list): Lista de diccionarios, cada uno representa un campo:
|
|
62
|
+
[
|
|
63
|
+
{"template": "input.html", "context": {"label": "Nombre", "valor": "Juan"}, "ancho": "50%"},
|
|
64
|
+
{"template": "input.html", "context": {"label": "Cédula", "valor": "1234"}, "ancho": "50%"}
|
|
65
|
+
]
|
|
66
|
+
"""
|
|
67
|
+
columns = []
|
|
68
|
+
for field in fields:
|
|
69
|
+
tpl = field.get("template")
|
|
70
|
+
ctx = field.get("context", {})
|
|
71
|
+
ancho = field.get("ancho", "100%")
|
|
72
|
+
flex = field.get("flex", "none")
|
|
73
|
+
|
|
74
|
+
# Renderizar el campo
|
|
75
|
+
rendered_field = self._render(tpl, ctx)
|
|
76
|
+
columns.append({
|
|
77
|
+
"ancho": ancho,
|
|
78
|
+
"flex": flex,
|
|
79
|
+
"content": rendered_field
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
# Renderizar la fila contenedora grid_row.html
|
|
83
|
+
row_html = self._render("grid_row.html", {"columns": columns})
|
|
84
|
+
self.components.append(row_html)
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
def add_raw_html(self, html):
|
|
88
|
+
"""Agrega HTML crudo al documento."""
|
|
89
|
+
self.components.append(html)
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
def add_page_break(self):
|
|
93
|
+
"""Inserta un salto de página."""
|
|
94
|
+
self.components.append('<div class="page-break"></div>')
|
|
95
|
+
return self
|
|
96
|
+
|
|
97
|
+
def build(self):
|
|
98
|
+
"""Une todos los componentes renderizados y compila el PDF."""
|
|
99
|
+
# 1. Obtener estilos globales (style.html)
|
|
100
|
+
head = self._render("style.html")
|
|
101
|
+
|
|
102
|
+
# 2. Combinar componentes
|
|
103
|
+
html_completo = "\n".join(self.components)
|
|
104
|
+
|
|
105
|
+
# 3. Construir estructura HTML final
|
|
106
|
+
html_final = f"""
|
|
107
|
+
<!DOCTYPE html>
|
|
108
|
+
<html lang="es">
|
|
109
|
+
<head>
|
|
110
|
+
<meta charset="UTF-8">
|
|
111
|
+
<title>Documento PDF</title>
|
|
112
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
|
113
|
+
{head}
|
|
114
|
+
</head>
|
|
115
|
+
<body>
|
|
116
|
+
{html_completo}
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
|
119
|
+
"""
|
|
120
|
+
return generate_pdf_from_string(html_final)
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from .base import BaseFormAssembler
|
|
2
|
+
|
|
3
|
+
class FormularioReposicionAssembler(BaseFormAssembler):
|
|
4
|
+
def __init__(self, context=None):
|
|
5
|
+
super().__init__(context)
|
|
6
|
+
self.page_number = 1
|
|
7
|
+
self.total_pages = 4
|
|
8
|
+
|
|
9
|
+
def build(self):
|
|
10
|
+
# ==========================================
|
|
11
|
+
# === PÁGINA 1: Datos y Radicado =========
|
|
12
|
+
# ==========================================
|
|
13
|
+
self.add_component('header.html', {
|
|
14
|
+
'titulo1': "FORMULARIO DE RECURSO DE REPOSICIÓN",
|
|
15
|
+
'titulo2': "CONTRA RESOLUCIONES DEL COMITÉ CERREM INDIVIDUAL Y COLECTIVO",
|
|
16
|
+
'titulo3': "UNIDAD NACIONAL DE PROTECCIÓN"
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
self.add_component('section.html', {'titulo': "Datos del radicado (Espacio para uso exclusivo de la UNP)"})
|
|
20
|
+
self.add_grid_row([
|
|
21
|
+
{"template": "input.html", "context": {"label": "1. Número del radicado *", "valor": self.context.get('numRadicado', '')}, "ancho": "33%"},
|
|
22
|
+
{"template": "input.html", "context": {"label": "2. Fecha de radicado *", "valor": self.context.get('fechaRadicado', '')}, "ancho": "33%"},
|
|
23
|
+
{"template": "input.html", "context": {"label": "3. Sede/Oficina", "valor": self.context.get('sedeRadicado', '')}, "ancho": "34%"}
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
self.add_component('section.html', {'titulo': "Fecha y lugar de diligenciamiento"})
|
|
27
|
+
self.add_grid_row([
|
|
28
|
+
{"template": "input.html", "context": {"label": "4. Fecha de diligenciamiento *", "valor": self.context.get('fechaDiligenciamiento', '')}, "ancho": "30%"},
|
|
29
|
+
{"template": "input.html", "context": {"label": "5. País *", "valor": self.context.get('paisDiligenciamiento', 'Colombia')}, "ancho": "20%"},
|
|
30
|
+
{"template": "input.html", "context": {"label": "6. Departamento *", "valor": self.context.get('dptoDiligenciamiento', '')}, "ancho": "25%"},
|
|
31
|
+
{"template": "input.html", "context": {"label": "7. Municipio *", "valor": self.context.get('mnpioDiligenciamiento', '')}, "ancho": "25%"}
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
self.add_component('section.html', {'titulo': "Resolución CERREM", 'icono': "fa-file-alt"})
|
|
35
|
+
self.add_grid_row([
|
|
36
|
+
{"template": "input.html", "context": {"label": "8. Número del radicado", "valor": self.context.get('numResolucion', '')}, "ancho": "50%"},
|
|
37
|
+
{"template": "input.html", "context": {"label": "9. Fecha de radicado", "valor": self.context.get('fechaResolucion', '')}, "ancho": "50%"}
|
|
38
|
+
])
|
|
39
|
+
|
|
40
|
+
self.add_component('section.html', {'titulo': "Datos Básicos", 'icono': "fa-user"})
|
|
41
|
+
self.add_grid_row([
|
|
42
|
+
{"template": "input.html", "context": {"label": "10. Primer nombre *", "valor": self.context.get('primerNombre', '')}, "ancho": "25%"},
|
|
43
|
+
{"template": "input.html", "context": {"label": "11. Segundo nombre", "valor": self.context.get('segundoNombre', '')}, "ancho": "25%"},
|
|
44
|
+
{"template": "input.html", "context": {"label": "12. Primer apellido *", "valor": self.context.get('primerApellido', '')}, "ancho": "25%"},
|
|
45
|
+
{"template": "input.html", "context": {"label": "13. Segundo apellido", "valor": self.context.get('segundoApellido', '')}, "ancho": "25%"}
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
self.add_component('checkbox.html', {
|
|
49
|
+
'label': "15. Género *",
|
|
50
|
+
'opciones': [
|
|
51
|
+
{'label': "Femenino", 'valor': "Femenino"},
|
|
52
|
+
{'label': "Masculino", 'valor': "Masculino"},
|
|
53
|
+
{'label': "No binario", 'valor': "No binario"}
|
|
54
|
+
],
|
|
55
|
+
'seleccionado': self.context.get('genero', '')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
self.add_component('section.html', {'titulo': "Número único de identificación personal (NUIP)", 'icono': "fa-id-card"})
|
|
59
|
+
self.add_grid_row([
|
|
60
|
+
{
|
|
61
|
+
"template": "checkbox.html",
|
|
62
|
+
"context": {
|
|
63
|
+
"label": "16. Tipo de Identificación *",
|
|
64
|
+
"opciones": [
|
|
65
|
+
{"label": "C.C.", "valor": "Cédula de ciudadanía"},
|
|
66
|
+
{"label": "C.E.", "valor": "Cédula de extranjería"},
|
|
67
|
+
{"label": "P.S.", "valor": "Pasaporte"},
|
|
68
|
+
{"label": "Otro", "valor": "Otro"}
|
|
69
|
+
],
|
|
70
|
+
"seleccionado": self.context.get('tipoIdentificacion', '')
|
|
71
|
+
},
|
|
72
|
+
"ancho": "45%"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"template": "input.html",
|
|
76
|
+
"context": {"label": "17. Número de identificación *", "valor": self.context.get('NumeroIdentificacion', '')},
|
|
77
|
+
"ancho": "27%"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"template": "input.html",
|
|
81
|
+
"context": {"label": "18. Fecha de expedición *", "valor": self.context.get('fechaExpedicion', '')},
|
|
82
|
+
"ancho": "28%"
|
|
83
|
+
}
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
self.add_component('section.html', {'titulo': "Datos de contacto", 'icono': "fa-envelope"})
|
|
87
|
+
self.add_grid_row([
|
|
88
|
+
{"template": "input.html", "context": {"label": "19. Celular uno *", "valor": self.context.get('celularUno', '')}, "ancho": "20%"},
|
|
89
|
+
{"template": "input.html", "context": {"label": "20. Celular dos", "valor": self.context.get('celularDos', '')}, "ancho": "20%"},
|
|
90
|
+
{"template": "input.html", "context": {"label": "21. Teléfono", "valor": self.context.get('telefono', '')}, "ancho": "20%"},
|
|
91
|
+
{"template": "input.html", "context": {"label": "22. Correo electrónico *", "valor": self.context.get('correo', '')}, "ancho": "40%"}
|
|
92
|
+
])
|
|
93
|
+
|
|
94
|
+
self.add_component('radio.html', {
|
|
95
|
+
'label': "23. ¿Autoriza el envío de notificaciones a través del correo electrónico inscrito?",
|
|
96
|
+
'opciones': [{'label': "Sí", 'valor': "Sí"}, {'label': "No", 'valor': "No"}],
|
|
97
|
+
'seleccionado': self.context.get('autoriza', '')
|
|
98
|
+
})
|
|
99
|
+
self.add_raw_html('<p style="font-size: 8px; color: #64748b; margin-top: -4px; margin-bottom: 12px;">* De conformidad con el artículo 56 de la Ley 1437 de 2011.</p>')
|
|
100
|
+
|
|
101
|
+
self.add_grid_row([
|
|
102
|
+
{
|
|
103
|
+
"template": "radio.html",
|
|
104
|
+
"context": {
|
|
105
|
+
"label": "24. ¿Presenta hechos sobrevinientes?",
|
|
106
|
+
"opciones": [{"label": "Sí", "valor": "Sí"}, {"label": "No", "valor": "No"}],
|
|
107
|
+
"seleccionado": self.context.get('presenta_hechos', '')
|
|
108
|
+
},
|
|
109
|
+
"ancho": "50%"
|
|
110
|
+
}
|
|
111
|
+
])
|
|
112
|
+
|
|
113
|
+
self.add_component('footer.html', {
|
|
114
|
+
'codigo': "GJU-FT-52-VX", 'fecha': "Oficialización: 00/00/0000", 'pagina': "Pág. 1 de 4"
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
# ==========================================
|
|
118
|
+
# === PÁGINA 2: Hechos Sobrevinientes =====
|
|
119
|
+
# ==========================================
|
|
120
|
+
self.add_page_break()
|
|
121
|
+
self.add_raw_html('<div class="bordered-page-container">')
|
|
122
|
+
self.add_component('section.html', {'titulo': "Hechos sobrevinientes que fundamentan el recurso", 'icono': "fa-edit"})
|
|
123
|
+
self.add_component('textarea.html', {
|
|
124
|
+
'alto': "780px",
|
|
125
|
+
'valor': self.context.get('hechosSobrevinientes', '')
|
|
126
|
+
})
|
|
127
|
+
self.add_raw_html('</div>')
|
|
128
|
+
self.add_component('footer.html', {
|
|
129
|
+
'codigo': "GJU-FT-52-VX", 'fecha': "Oficialización: 00/00/0000", 'pagina': "Pág. 2 de 4"
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
# ==========================================
|
|
133
|
+
# === PÁGINA 3: Medidas y Apoderado =======
|
|
134
|
+
# ==========================================
|
|
135
|
+
self.add_page_break()
|
|
136
|
+
self.add_raw_html('<div class="bordered-page-container">')
|
|
137
|
+
|
|
138
|
+
self.add_component('section.html', {'titulo': "Medidas de Protección", 'icono': "fa-shield-alt"})
|
|
139
|
+
self.add_component('radio.html', {
|
|
140
|
+
'label': "32. ¿Es beneficiario de medidas de protección por la UNP?",
|
|
141
|
+
'opciones': [{'label': "Sí", 'valor': "Sí"}, {'label': "No", 'valor': "No"}],
|
|
142
|
+
'seleccionado': self.context.get('tiene_medidas', '')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
self.add_component('section.html', {'titulo': "Datos apoderado"})
|
|
146
|
+
self.add_component('radio.html', {
|
|
147
|
+
'label': "33. ¿Presenta recurso mediante apoderado?",
|
|
148
|
+
'opciones': [{'label': "Sí", 'valor': "Sí"}, {'label': "No", 'valor': "No"}],
|
|
149
|
+
'seleccionado': self.context.get('presentaApoderado', '')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
self.add_component('section.html', {'titulo': "Nombre(s) y apellido(s) del apoderado", 'icono': "fa-user-tie"})
|
|
153
|
+
self.add_grid_row([
|
|
154
|
+
{"template": "input.html", "context": {"label": "34. Primer nombre *", "valor": self.context.get('primerNombreApoderado', '')}, "ancho": "25%"},
|
|
155
|
+
{"template": "input.html", "context": {"label": "35. Segundo nombre", "valor": self.context.get('segundoNombreApoderado', '')}, "ancho": "25%"},
|
|
156
|
+
{"template": "input.html", "context": {"label": "36. Primer apellido *", "valor": self.context.get('primerApellidoApoderado', '')}, "ancho": "25%"},
|
|
157
|
+
{"template": "input.html", "context": {"label": "37. Segundo apellido", "valor": self.context.get('segundoApellidoApoderado', '')}, "ancho": "25%"}
|
|
158
|
+
])
|
|
159
|
+
|
|
160
|
+
self.add_grid_row([
|
|
161
|
+
{
|
|
162
|
+
"template": "checkbox.html",
|
|
163
|
+
"context": {
|
|
164
|
+
"label": "38. Tipo de Identificación",
|
|
165
|
+
"opciones": [
|
|
166
|
+
{"label": "C.C.", "valor": "Cédula de ciudadanía"},
|
|
167
|
+
{"label": "C.E.", "valor": "Cédula de extranjería"},
|
|
168
|
+
{"label": "Pasaporte", "valor": "Pasaporte"}
|
|
169
|
+
],
|
|
170
|
+
"seleccionado": self.context.get('tipoIdentificacionApoderado', '')
|
|
171
|
+
},
|
|
172
|
+
"ancho": "40%"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"template": "input.html",
|
|
176
|
+
"context": {"label": "39. Número de identificación *", "valor": self.context.get('numeroIdentificacionApoderado', '')},
|
|
177
|
+
"ancho": "30%"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"template": "input.html",
|
|
181
|
+
"context": {"label": "40. Tarjeta Profesional *", "valor": self.context.get('tarjetaProfesional', '')},
|
|
182
|
+
"ancho": "30%"
|
|
183
|
+
}
|
|
184
|
+
])
|
|
185
|
+
|
|
186
|
+
self.add_component('section.html', {'titulo': "Inconformidad", 'icono': "fa-exclamation-circle"})
|
|
187
|
+
self.add_component('textarea.html', {
|
|
188
|
+
'label': "41. Descripción clara de los motivos de inconformidad",
|
|
189
|
+
'alto': "380px",
|
|
190
|
+
'valor': self.context.get('motivoInconformidad', '')
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
self.add_raw_html('</div>')
|
|
194
|
+
self.add_component('footer.html', {
|
|
195
|
+
'codigo': "GJU-FT-52-VX", 'fecha': "Oficialización: 00/00/0000", 'pagina': "Pág. 3 de 4"
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
# ==========================================
|
|
199
|
+
# === PÁGINA 4: Anexos y Firma ============
|
|
200
|
+
# ==========================================
|
|
201
|
+
self.add_page_break()
|
|
202
|
+
self.add_raw_html('<div class="bordered-page-container">')
|
|
203
|
+
|
|
204
|
+
self.add_component('section.html', {'titulo': "Anexos adjuntos", 'icono': "fa-paperclip"})
|
|
205
|
+
self.add_component('checkbox.html', {
|
|
206
|
+
'label': "Marque los anexos que adjunta a este recurso:",
|
|
207
|
+
'opciones': [
|
|
208
|
+
{'label': "Copia del documento de identidad", 'valor': "identidad"},
|
|
209
|
+
{'label': "Poder debidamente otorgado (si aplica)", 'valor': "poder"},
|
|
210
|
+
{'label': "Pruebas documentales que sustentan la inconformidad", 'valor': "pruebas"},
|
|
211
|
+
{'label': "Otros documentos de interés", 'valor': "otros"}
|
|
212
|
+
],
|
|
213
|
+
'seleccionados': self.context.get('anexos_seleccionados', [])
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
self.add_raw_html('''
|
|
217
|
+
<div style="margin-top: 25px; margin-bottom: 25px; padding: 12px; border: 1px solid #cbd5e1; border-radius: 4px; font-size: 10px; color: #334155; background-color: #f8fafc; line-height: 1.4;">
|
|
218
|
+
<strong>42. MANIFIESTO BAJO LA GRAVEDAD DEL JURAMENTO *</strong><br>
|
|
219
|
+
Manifiesto bajo la gravedad del juramento que la información suministrada en este formulario es verídica y que los documentos adjuntos son copia fiel de sus originales.
|
|
220
|
+
</div>
|
|
221
|
+
''')
|
|
222
|
+
|
|
223
|
+
self.add_component('section.html', {'titulo': "Firma del solicitante / apoderado"})
|
|
224
|
+
|
|
225
|
+
firmas = [
|
|
226
|
+
{
|
|
227
|
+
'nombre': self.context.get('nombreCompleto', 'Firma del Solicitante'),
|
|
228
|
+
'cargo': "Solicitante",
|
|
229
|
+
'documento': self.context.get('NumeroIdentificacion', '')
|
|
230
|
+
}
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
if self.context.get('presentaApoderado') == 'Sí':
|
|
234
|
+
firmas.append({
|
|
235
|
+
'nombre': self.context.get('nombreCompletoApoderado', 'Firma del Apoderado'),
|
|
236
|
+
'cargo': "Apoderado",
|
|
237
|
+
'documento': self.context.get('numeroIdentificacionApoderado', '')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
self.add_component('signature.html', {
|
|
241
|
+
'firmas': firmas,
|
|
242
|
+
'margin_top': "60px"
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
self.add_raw_html('</div>')
|
|
246
|
+
|
|
247
|
+
self.add_raw_html('''
|
|
248
|
+
<div style="font-size: 8px; color: #64748b; text-align: center; margin-top: 10px;">
|
|
249
|
+
Archívese en: Carpeta única del solicitante - Unidad Nacional de Protección.
|
|
250
|
+
</div>
|
|
251
|
+
''')
|
|
252
|
+
|
|
253
|
+
self.add_component('footer.html', {
|
|
254
|
+
'codigo': "GJU-FT-52-VX", 'fecha': "Oficialización: 00/00/0000", 'pagina': "Pág. 4 de 4"
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
return super().build()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from .base import BaseFormAssembler
|
|
2
|
+
from assemblerpdf.core import generate_pdf_from_string
|
|
3
|
+
|
|
4
|
+
class TwoColumnReportAssembler(BaseFormAssembler):
|
|
5
|
+
def __init__(self, context=None):
|
|
6
|
+
super().__init__(context)
|
|
7
|
+
|
|
8
|
+
def add_section(self, title, content_html):
|
|
9
|
+
"""Agrega una sección con título y contenido en dos columnas."""
|
|
10
|
+
section_html = f"""
|
|
11
|
+
<div style="margin: 20px 0; padding: 15px; border: 1px solid #cbd5e1; border-radius: 8px; background-color: #ffffff;">
|
|
12
|
+
<h3 style="color: #002857; margin-top: 0; margin-bottom: 10px; font-size: 14px; font-weight: bold; border-bottom: 1px solid #cbd5e1; padding-bottom: 5px;">{title}</h3>
|
|
13
|
+
<div style="display: flex; gap: 20px;">
|
|
14
|
+
{content_html}
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
"""
|
|
18
|
+
self.components.append(section_html)
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
def build(self):
|
|
22
|
+
# Renderizar head global (style.html)
|
|
23
|
+
head = self._render("style.html")
|
|
24
|
+
|
|
25
|
+
# Combinar todos los componentes
|
|
26
|
+
html_completo = "\n".join(self.components)
|
|
27
|
+
|
|
28
|
+
# Plantilla base del informe
|
|
29
|
+
html_final = f"""
|
|
30
|
+
<!DOCTYPE html>
|
|
31
|
+
<html lang="es">
|
|
32
|
+
<head>
|
|
33
|
+
<meta charset="UTF-8">
|
|
34
|
+
<title>Informe</title>
|
|
35
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
|
36
|
+
{head}
|
|
37
|
+
</head>
|
|
38
|
+
<body style="padding: 20px; background-color: #f8fafc;">
|
|
39
|
+
<h1 style="color: #002857; text-align: center; margin-bottom: 20px; font-size: 18px; font-weight: bold; border-bottom: 2px solid #002857; padding-bottom: 10px;">Informe</h1>
|
|
40
|
+
{html_completo}
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|
|
43
|
+
"""
|
|
44
|
+
return generate_pdf_from_string(html_final)
|