diff --git a/backend/core/admin.py b/backend/core/admin.py index d3b2467..0647ada 100644 --- a/backend/core/admin.py +++ b/backend/core/admin.py @@ -1,5 +1,8 @@ from django.contrib import admin -from .models import Document, DocumentSegment, Organization, Risk, Control +from .models import Document, DocumentSegment, Organization, Risk, Control, DocumentTemplate +from django.urls import reverse +from django.utils.html import format_html + class DocumentSegmentInline(admin.StackedInline): model = DocumentSegment @@ -12,7 +15,18 @@ class DocumentAdmin(admin.ModelAdmin): list_display = ('organization', 'created_at', 'modified_at') search_fields = ['organization__name'] readonly_fields = ('created_at', 'modified_at') - + +class DocumentTemplateAdmin(admin.ModelAdmin): + list_display = ['name', 'created_at', 'updated_at', 'preview_button'] + + def preview_button(self, obj): + url = reverse('core:template_preview', args=[obj.name]) + return format_html('Preview', url) + + preview_button.short_description = 'Preview' + preview_button.allow_tags = True + + class OrganizationAdmin(admin.ModelAdmin): list_display = ('name', 'email', 'industry_sector') search_fields = ['name', 'email'] @@ -31,3 +45,4 @@ admin.site.register(Document, DocumentAdmin) admin.site.register(Organization, OrganizationAdmin) admin.site.register(Risk ,RiskAdmin) admin.site.register(Control, ControlAdmin) +admin.site.register(DocumentTemplate, DocumentTemplateAdmin) diff --git a/backend/core/management/commands/export_template.py b/backend/core/management/commands/export_template.py new file mode 100644 index 0000000..6c9cef5 --- /dev/null +++ b/backend/core/management/commands/export_template.py @@ -0,0 +1,22 @@ +import yaml +from django.core.management.base import BaseCommand +from backend.core.models import DocumentTemplate + +class Command(BaseCommand): + help = "Export document template to a YAML file" + + def add_arguments(self, parser): + parser.add_argument('yaml_file', type=str, help="The YAML file to export template to") + + def handle(self, *args, **options): + yaml_file_path = options['yaml_file'] + + template = DocumentTemplate.objects.first() + + if template: + with open(yaml_file_path, 'w') as yaml_file: + yaml_file.write(template.content) + + self.stdout.write(self.style.SUCCESS('Template exported successfully')) + else: + self.stdout.write(self.style.ERROR('No template found in the database')) diff --git a/backend/core/management/commands/import_template.py b/backend/core/management/commands/import_template.py new file mode 100644 index 0000000..d055293 --- /dev/null +++ b/backend/core/management/commands/import_template.py @@ -0,0 +1,23 @@ +import yaml +from django.core.management.base import BaseCommand +from backend.core.models import DocumentTemplate + +class Command(BaseCommand): + help = "Import document template from a YAML file" + + def add_arguments(self, parser): + parser.add_argument('yaml_file', type=str, help="YAML file to import template from") + + def handle(self, *args, **options): + yaml_file_path = options['yaml_file'] + + with open(yaml_file_path, 'r') as file: + content = file.read() + yaml_data = yaml.safe_load(content) + + DocumentTemplate.objects.update_or_create( + name="Default Template", + defaults={"content": content} + ) + + self.stdout.write(self.style.SUCCESS("Template imported successfully.")) diff --git a/backend/core/management/commands/tests/test_export_template.py b/backend/core/management/commands/tests/test_export_template.py new file mode 100644 index 0000000..4b05734 --- /dev/null +++ b/backend/core/management/commands/tests/test_export_template.py @@ -0,0 +1,34 @@ +import os +from io import StringIO +from django.core.management import call_command +from django.test import TestCase +from backend.core.models import DocumentTemplate + + +class DocumentTemplateExportCommandTest(TestCase): + def setUp(self): + self.template_content = """ + - segment_type: "title" + content: "Document Title" + - segment_type: "subtitle" + content: "Document Subtitle" + """ + self.template = DocumentTemplate.objects.create( + name="Default Template", + content=self.template_content + ) + self.export_file = 'exported_template.yaml' + + def tearDown(self): + if os.path.exists(self.export_file): + os.remove(self.export_file) + + def test_export_template(self): + out = StringIO() + call_command('export_template', self.export_file, stdout=out) + + self.assertIn("Template exported successfully", out.getvalue()) + + with open(self.export_file, 'r') as f: + content = f.read() + self.assertEqual(content.strip(), self.template_content.strip()) diff --git a/backend/core/management/commands/tests/test_import_template.py b/backend/core/management/commands/tests/test_import_template.py new file mode 100644 index 0000000..12df27e --- /dev/null +++ b/backend/core/management/commands/tests/test_import_template.py @@ -0,0 +1,31 @@ +import os +from io import StringIO +from django.core.management import call_command +from django.test import TestCase +from backend.core.models import DocumentTemplate + + +class DocumentTemplateImportCommandTest(TestCase): + def setUp(self): + self.yaml_content = """ + - segment_type: "title" + content: "Document Title" + - segment_type: "subtitle" + content: "Document Subtitle" + """ + self.yaml_file = 'test_template.yaml' + with open(self.yaml_file, 'w') as f: + f.write(self.yaml_content) + + def tearDown(self): + if os.path.exists(self.yaml_file): + os.remove(self.yaml_file) + + def test_import_template(self): + out = StringIO() + call_command('import_template', self.yaml_file, stdout=out) + + self.assertIn("Template imported successfully", out.getvalue()) + + template = DocumentTemplate.objects.get(name="Default Template") + self.assertEqual(template.content.strip(), self.yaml_content.strip()) diff --git a/backend/core/migrations/0006_documenttemplate.py b/backend/core/migrations/0006_documenttemplate.py new file mode 100644 index 0000000..e7fe127 --- /dev/null +++ b/backend/core/migrations/0006_documenttemplate.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.3 on 2025-02-13 16:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_control'), + ] + + operations = [ + migrations.CreateModel( + name='DocumentTemplate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ('content', models.TextField(help_text='YAML format content')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index bc25609..c1531a5 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -4,6 +4,7 @@ from django.contrib.auth.models import User from django.db import models from localflavor.br.br_states import STATE_CHOICES from django.contrib import admin +import yaml class UuidModel(models.Model): @@ -124,6 +125,16 @@ class Document(models.Model): content=content, order=new_order ) + + +class DocumentTemplate(models.Model): + name = models.CharField(max_length=255, unique=True) + content = models.TextField(help_text="YAML format content") + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def to_dict(self): + return yaml.safe_load(self.content) class Risk(models.Model): diff --git a/backend/core/templates/template_preview.html b/backend/core/templates/template_preview.html new file mode 100644 index 0000000..209a36f --- /dev/null +++ b/backend/core/templates/template_preview.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% block content %} +
{{ segment.content|safe }}
+ {% elif segment.segment_type == "quote" %} +{{ segment.content }}+ {% endif %} + {% endfor %} +{% endblock %} diff --git a/backend/core/urls.py b/backend/core/urls.py index 0b6d5e0..1b67d6c 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -10,4 +10,5 @@ urlpatterns = [ path('thankyou/', v.thankyou, name='thankyou'), # url document/ recieves a parameter named 'uuid' and passes it to the view path('document/
This is a static section with an embedded HTML table:
+| Header 1 | +Header 2 | +
|---|---|
| Data 1 | +Data 2 | +