iil-doc-templates 0.1.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.
@@ -0,0 +1,50 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *\.class
5
+ .venv/
6
+ venv/
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ # Django
11
+ *.log
12
+ db.sqlite3
13
+ media/
14
+ staticfiles/
15
+ # Environment
16
+ .env
17
+ .env.local
18
+ .env.prod
19
+
20
+ # Secrets (ADR-045) — NEVER commit plaintext
21
+ secrets.env
22
+ !secrets.enc.env
23
+ # IDE
24
+ .idea/
25
+ .vscode/
26
+ *.swp
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+ # Large files
31
+ *.sqlite3
32
+ *.log
33
+ output_batch/
34
+ app_backup/
35
+ docs_legacy/
36
+ # Sphinx build output
37
+ docs/_build/
38
+ *:Zone.Identifier
39
+ # Local Windsurf/MCP config — machine-specific, never commit
40
+ .windsurf/mcp_config.json
41
+ # Local infra plaintext secrets — NEVER commit
42
+ infra/*.env
43
+ infra/*.key
44
+ # Validate-ports CI draft (not yet integrated)
45
+ .github/workflows/validate-ports.yml
46
+ # Markdownlint local config override
47
+ .markdownlint.json
48
+ # Concept review drafts (local working copies, not for VCS)
49
+ docs/concepts/REVIEW-*
50
+ env_loader.py
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: iil-doc-templates
3
+ Version: 0.1.0
4
+ Summary: Reusable Django document template system — create, edit, fill templates with PDF extraction and LLM prefill
5
+ Author-email: Achim Dehnert <achim.dehnert@iil.gmbh>
6
+ License-Expression: LicenseRef-Proprietary
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: django>=4.2
9
+ Requires-Dist: iil-concept-templates>=0.4.0
10
+ Provides-Extra: all
11
+ Requires-Dist: iil-aifw>=0.5.0; extra == 'all'
12
+ Requires-Dist: iil-django-tenancy>=0.3.0; extra == 'all'
13
+ Requires-Dist: pdfplumber>=0.10; extra == 'all'
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == 'dev'
16
+ Requires-Dist: pytest-django; extra == 'dev'
17
+ Requires-Dist: ruff; extra == 'dev'
18
+ Provides-Extra: llm
19
+ Requires-Dist: iil-aifw>=0.5.0; extra == 'llm'
20
+ Provides-Extra: pdf
21
+ Requires-Dist: pdfplumber>=0.10; extra == 'pdf'
22
+ Provides-Extra: tenancy
23
+ Requires-Dist: iil-django-tenancy>=0.3.0; extra == 'tenancy'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # iil-doc-templates
27
+
28
+ Reusable Django document template system — create, edit, fill templates with PDF extraction and LLM prefill.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install iil-doc-templates
34
+ ```
35
+
36
+ With optional dependencies:
37
+ ```bash
38
+ pip install iil-doc-templates[pdf] # pdfplumber for PDF extraction
39
+ pip install iil-doc-templates[llm] # iil-aifw for LLM prefill
40
+ pip install iil-doc-templates[all] # everything
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ # settings.py
47
+ INSTALLED_APPS = [
48
+ ...
49
+ "doc_templates",
50
+ ]
51
+
52
+ # urls.py
53
+ path("doc-templates/", include("doc_templates.urls")),
54
+ ```
55
+
56
+ Then run migrations:
57
+ ```bash
58
+ python manage.py migrate doc_templates
59
+ ```
60
+
61
+ ## Features
62
+
63
+ - **DocumentTemplate** — Reusable document templates with JSON structure (sections + fields)
64
+ - **DocumentInstance** — Filled-out documents based on templates
65
+ - **PDF Upload** — Extract structure from PDF using `iil-concept-templates`
66
+ - **Interactive Editor** — Drag & drop sections, CRUD fields, table columns
67
+ - **LLM Prefill** — AI-powered field completion via `iil-aifw`
68
+ - **PDF Export** — Generate PDF from filled documents via `concept_templates.document_renderer`
69
+ - **Multi-Tenant** — All queries filter by `tenant_id`
70
+
71
+ ## Architecture
72
+
73
+ ```
74
+ iil-concept-templates (Pure Python) iil-doc-templates (Django)
75
+ ├── Pydantic Schemas ├── Django Models
76
+ ├── PDF Structure Extraction ──► ├── CRUD Views + URLs
77
+ ├── Analysis, Prefill, Export ├── HTML Templates (6)
78
+ └── No Django dependency ├── Admin Integration
79
+ └── Uses concept_templates internally
80
+ ```
81
+
82
+ ## Field Types
83
+
84
+ | Type | Description |
85
+ |------|-------------|
86
+ | `textarea` | Multi-line text (default) |
87
+ | `text` | Single-line text |
88
+ | `number` | Numeric input |
89
+ | `date` | Date picker |
90
+ | `boolean` | Yes/No toggle |
91
+ | `table` | Table with configurable columns |
92
+
93
+ ## URL Patterns
94
+
95
+ | URL | View | Description |
96
+ |-----|------|-------------|
97
+ | `/` | `template-list` | List all templates + instances |
98
+ | `/create/` | `template-create` | Create empty template |
99
+ | `/upload/` | `template-upload` | Create template from PDF |
100
+ | `/<pk>/edit/` | `template-edit` | Interactive template editor |
101
+ | `/<pk>/delete/` | `template-delete` | Delete template |
102
+ | `/<pk>/instance/create/` | `instance-create` | Create document from template |
103
+ | `/instance/<pk>/edit/` | `instance-edit` | Edit document fields |
104
+ | `/instance/<pk>/delete/` | `instance-delete` | Delete document |
105
+ | `/instance/<pk>/prefill/` | `instance-prefill` | LLM prefill endpoint |
106
+ | `/instance/<pk>/pdf/` | `instance-pdf` | PDF export |
107
+
108
+ ## Dependencies
109
+
110
+ - **Required**: Django ≥ 4.2, iil-concept-templates ≥ 0.4.0
111
+ - **Optional**: pdfplumber (PDF extraction), iil-aifw (LLM prefill)
@@ -0,0 +1,86 @@
1
+ # iil-doc-templates
2
+
3
+ Reusable Django document template system — create, edit, fill templates with PDF extraction and LLM prefill.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install iil-doc-templates
9
+ ```
10
+
11
+ With optional dependencies:
12
+ ```bash
13
+ pip install iil-doc-templates[pdf] # pdfplumber for PDF extraction
14
+ pip install iil-doc-templates[llm] # iil-aifw for LLM prefill
15
+ pip install iil-doc-templates[all] # everything
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```python
21
+ # settings.py
22
+ INSTALLED_APPS = [
23
+ ...
24
+ "doc_templates",
25
+ ]
26
+
27
+ # urls.py
28
+ path("doc-templates/", include("doc_templates.urls")),
29
+ ```
30
+
31
+ Then run migrations:
32
+ ```bash
33
+ python manage.py migrate doc_templates
34
+ ```
35
+
36
+ ## Features
37
+
38
+ - **DocumentTemplate** — Reusable document templates with JSON structure (sections + fields)
39
+ - **DocumentInstance** — Filled-out documents based on templates
40
+ - **PDF Upload** — Extract structure from PDF using `iil-concept-templates`
41
+ - **Interactive Editor** — Drag & drop sections, CRUD fields, table columns
42
+ - **LLM Prefill** — AI-powered field completion via `iil-aifw`
43
+ - **PDF Export** — Generate PDF from filled documents via `concept_templates.document_renderer`
44
+ - **Multi-Tenant** — All queries filter by `tenant_id`
45
+
46
+ ## Architecture
47
+
48
+ ```
49
+ iil-concept-templates (Pure Python) iil-doc-templates (Django)
50
+ ├── Pydantic Schemas ├── Django Models
51
+ ├── PDF Structure Extraction ──► ├── CRUD Views + URLs
52
+ ├── Analysis, Prefill, Export ├── HTML Templates (6)
53
+ └── No Django dependency ├── Admin Integration
54
+ └── Uses concept_templates internally
55
+ ```
56
+
57
+ ## Field Types
58
+
59
+ | Type | Description |
60
+ |------|-------------|
61
+ | `textarea` | Multi-line text (default) |
62
+ | `text` | Single-line text |
63
+ | `number` | Numeric input |
64
+ | `date` | Date picker |
65
+ | `boolean` | Yes/No toggle |
66
+ | `table` | Table with configurable columns |
67
+
68
+ ## URL Patterns
69
+
70
+ | URL | View | Description |
71
+ |-----|------|-------------|
72
+ | `/` | `template-list` | List all templates + instances |
73
+ | `/create/` | `template-create` | Create empty template |
74
+ | `/upload/` | `template-upload` | Create template from PDF |
75
+ | `/<pk>/edit/` | `template-edit` | Interactive template editor |
76
+ | `/<pk>/delete/` | `template-delete` | Delete template |
77
+ | `/<pk>/instance/create/` | `instance-create` | Create document from template |
78
+ | `/instance/<pk>/edit/` | `instance-edit` | Edit document fields |
79
+ | `/instance/<pk>/delete/` | `instance-delete` | Delete document |
80
+ | `/instance/<pk>/prefill/` | `instance-prefill` | LLM prefill endpoint |
81
+ | `/instance/<pk>/pdf/` | `instance-pdf` | PDF export |
82
+
83
+ ## Dependencies
84
+
85
+ - **Required**: Django ≥ 4.2, iil-concept-templates ≥ 0.4.0
86
+ - **Optional**: pdfplumber (PDF extraction), iil-aifw (LLM prefill)
@@ -0,0 +1,11 @@
1
+ """iil-doc-templates — Reusable Django document template system.
2
+
3
+ Provides DocumentTemplate + DocumentInstance models, CRUD views,
4
+ interactive template editor, PDF extraction, and LLM prefill.
5
+
6
+ Uses iil-concept-templates for schemas and PDF structure extraction.
7
+ """
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ default_app_config = "doc_templates.apps.DocTemplatesConfig"
@@ -0,0 +1,22 @@
1
+ """Admin registration for doc_templates."""
2
+
3
+ from django.contrib import admin
4
+
5
+ from .models import DocumentInstance, DocumentTemplate
6
+
7
+
8
+ @admin.register(DocumentTemplate)
9
+ class DocumentTemplateAdmin(admin.ModelAdmin):
10
+ list_display = ["name", "scope", "status", "section_count", "field_count", "updated_at"]
11
+ list_filter = ["status", "scope"]
12
+ search_fields = ["name", "description"]
13
+ readonly_fields = ["uuid", "created_at", "updated_at"]
14
+
15
+
16
+ @admin.register(DocumentInstance)
17
+ class DocumentInstanceAdmin(admin.ModelAdmin):
18
+ list_display = ["name", "template", "status", "updated_at"]
19
+ list_filter = ["status"]
20
+ search_fields = ["name"]
21
+ readonly_fields = ["uuid", "created_at", "updated_at"]
22
+ raw_id_fields = ["template"]
@@ -0,0 +1,9 @@
1
+ """Django app config for doc_templates."""
2
+
3
+ from django.apps import AppConfig
4
+
5
+
6
+ class DocTemplatesConfig(AppConfig):
7
+ name = "doc_templates"
8
+ verbose_name = "Dokumentvorlagen"
9
+ default_auto_field = "django.db.models.BigAutoField"
@@ -0,0 +1,156 @@
1
+ """Document template models (generalized from ExDocTemplate/ExDocInstance)."""
2
+
3
+ import json
4
+ import uuid
5
+
6
+ from django.db import models
7
+
8
+ # Graceful import for TenantManager
9
+ try:
10
+ from django_tenancy.managers import TenantManager
11
+ except ImportError:
12
+ TenantManager = models.Manager
13
+
14
+
15
+ class DocumentTemplate(models.Model):
16
+ """Wiederverwendbare Dokumentvorlage (modulübergreifend).
17
+
18
+ Struktur als JSON:
19
+ {
20
+ "sections": [
21
+ {
22
+ "key": "section_1",
23
+ "label": "1. Allgemeines",
24
+ "fields": [
25
+ {"key": "inhalt", "label": "Inhalt", "type": "textarea"}
26
+ ]
27
+ }
28
+ ]
29
+ }
30
+ """
31
+
32
+ class Status(models.TextChoices):
33
+ DRAFT = "draft", "Entwurf"
34
+ ACCEPTED = "accepted", "Akzeptiert"
35
+ ARCHIVED = "archived", "Archiviert"
36
+
37
+ uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
38
+ tenant_id = models.UUIDField(db_index=True)
39
+ name = models.CharField(max_length=255)
40
+ description = models.TextField(blank=True, default="")
41
+ scope = models.CharField(
42
+ max_length=50, blank=True, default="",
43
+ help_text="Fachbereich, z.B. explosionsschutz, brandschutz, gbu, risk",
44
+ )
45
+ structure_json = models.TextField(
46
+ default='{"sections": []}',
47
+ help_text="Template-Struktur als JSON",
48
+ )
49
+ status = models.CharField(
50
+ max_length=20, choices=Status.choices, default=Status.DRAFT,
51
+ )
52
+ source_filename = models.CharField(max_length=255, blank=True, default="")
53
+ source_text = models.TextField(blank=True, default="")
54
+
55
+ created_at = models.DateTimeField(auto_now_add=True)
56
+ updated_at = models.DateTimeField(auto_now=True)
57
+
58
+ objects = TenantManager()
59
+
60
+ class Meta:
61
+ db_table = "doc_template"
62
+ verbose_name = "Dokumentvorlage"
63
+ verbose_name_plural = "Dokumentvorlagen"
64
+ ordering = ["-updated_at"]
65
+ indexes = [
66
+ models.Index(fields=["tenant_id", "status"], name="ix_doctmpl_tenant_st"),
67
+ models.Index(fields=["tenant_id", "scope"], name="ix_doctmpl_tenant_scope"),
68
+ ]
69
+
70
+ def __str__(self) -> str:
71
+ return f"{self.name} ({self.get_status_display()})"
72
+
73
+ @property
74
+ def section_count(self) -> int:
75
+ try:
76
+ data = json.loads(self.structure_json)
77
+ return len(data.get("sections", []))
78
+ except (json.JSONDecodeError, TypeError):
79
+ return 0
80
+
81
+ @property
82
+ def field_count(self) -> int:
83
+ try:
84
+ data = json.loads(self.structure_json)
85
+ return sum(len(s.get("fields", [])) for s in data.get("sections", []))
86
+ except (json.JSONDecodeError, TypeError):
87
+ return 0
88
+
89
+ def get_structure(self) -> dict:
90
+ """Parse and return structure from JSON."""
91
+ try:
92
+ return json.loads(self.structure_json)
93
+ except (json.JSONDecodeError, TypeError):
94
+ return {"sections": []}
95
+
96
+ def get_sections(self) -> list[dict]:
97
+ """Return sections list from structure."""
98
+ return self.get_structure().get("sections", [])
99
+
100
+
101
+ class DocumentInstance(models.Model):
102
+ """Ausgefülltes Dokument basierend auf einem Template.
103
+
104
+ Werte als JSON:
105
+ {
106
+ "section_1": {
107
+ "inhalt": "Dieses Dokument..."
108
+ }
109
+ }
110
+ """
111
+
112
+ class Status(models.TextChoices):
113
+ DRAFT = "draft", "Entwurf"
114
+ REVIEW = "review", "In Prüfung"
115
+ APPROVED = "approved", "Freigegeben"
116
+
117
+ uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
118
+ tenant_id = models.UUIDField(db_index=True)
119
+ template = models.ForeignKey(
120
+ DocumentTemplate, on_delete=models.PROTECT, related_name="instances",
121
+ )
122
+ name = models.CharField(max_length=255)
123
+ values_json = models.TextField(
124
+ default="{}", help_text="Ausgefüllte Werte als JSON",
125
+ )
126
+ status = models.CharField(
127
+ max_length=20, choices=Status.choices, default=Status.DRAFT,
128
+ )
129
+ source_filename = models.CharField(
130
+ max_length=255, blank=True, default="",
131
+ help_text="Dateiname des importierten Dokuments",
132
+ )
133
+
134
+ created_at = models.DateTimeField(auto_now_add=True)
135
+ updated_at = models.DateTimeField(auto_now=True)
136
+
137
+ objects = TenantManager()
138
+
139
+ class Meta:
140
+ db_table = "doc_instance"
141
+ verbose_name = "Dokument"
142
+ verbose_name_plural = "Dokumente"
143
+ ordering = ["-updated_at"]
144
+ indexes = [
145
+ models.Index(fields=["tenant_id", "status"], name="ix_docinst_tenant_st"),
146
+ ]
147
+
148
+ def __str__(self) -> str:
149
+ return f"{self.name} ({self.get_status_display()})"
150
+
151
+ def get_values(self) -> dict:
152
+ """Parse and return values from JSON."""
153
+ try:
154
+ return json.loads(self.values_json)
155
+ except (json.JSONDecodeError, TypeError):
156
+ return {}
@@ -0,0 +1,48 @@
1
+ {% extends "base.html" %}
2
+ {% block title %}Leere Vorlage erstellen{% endblock %}
3
+
4
+ {% block content %}
5
+ <div class="max-w-2xl mx-auto px-4 py-8">
6
+
7
+ <div class="flex items-center space-x-4 mb-6">
8
+ <a href="{% url 'doc_templates:template-list' %}" class="text-gray-400 hover:text-gray-600">&larr;</a>
9
+ <h1 class="text-2xl font-bold text-gray-900">Leere Vorlage erstellen</h1>
10
+ </div>
11
+
12
+ <div class="bg-white rounded-xl border border-gray-200 p-6">
13
+ <form method="post" class="space-y-4">
14
+ {% csrf_token %}
15
+ <div>
16
+ <label class="block text-sm font-medium text-gray-700 mb-1">Name der Vorlage *</label>
17
+ <input type="text" name="name" required placeholder="z.B. Explosionsschutzdokument nach TRBS 2152"
18
+ class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">
19
+ </div>
20
+ <div>
21
+ <label class="block text-sm font-medium text-gray-700 mb-1">Fachbereich</label>
22
+ <input type="text" name="scope" list="scope-options" placeholder="z.B. explosionsschutz, brandschutz, gbu"
23
+ class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">
24
+ <datalist id="scope-options">
25
+ <option value="explosionsschutz">
26
+ <option value="brandschutz">
27
+ <option value="gbu">
28
+ <option value="betriebsanweisung">
29
+ <option value="risk">
30
+ </datalist>
31
+ </div>
32
+ <div>
33
+ <label class="block text-sm font-medium text-gray-700 mb-1">Beschreibung (optional)</label>
34
+ <textarea name="description" rows="2"
35
+ class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500"
36
+ placeholder="Kurze Beschreibung..."></textarea>
37
+ </div>
38
+ <div class="flex justify-end gap-3 pt-2">
39
+ <a href="{% url 'doc_templates:template-list' %}" class="px-4 py-2 text-sm text-gray-500 hover:text-gray-700">Abbrechen</a>
40
+ <button type="submit" class="px-5 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium">
41
+ Vorlage erstellen
42
+ </button>
43
+ </div>
44
+ </form>
45
+ </div>
46
+
47
+ </div>
48
+ {% endblock %}