Resolve "Napraviti template za dokument" #56
@@ -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('<a href="{}" target="_blank">Preview</a>', 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)
|
||||
|
||||
22
backend/core/management/commands/export_template.py
Normal file
22
backend/core/management/commands/export_template.py
Normal file
@@ -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'))
|
||||
23
backend/core/management/commands/import_template.py
Normal file
23
backend/core/management/commands/import_template.py
Normal file
@@ -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."))
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
23
backend/core/migrations/0006_documenttemplate.py
Normal file
23
backend/core/migrations/0006_documenttemplate.py
Normal file
@@ -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)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -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):
|
||||
|
||||
22
backend/core/templates/template_preview.html
Normal file
22
backend/core/templates/template_preview.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Template Preview</h1>
|
||||
{% for segment in template %}
|
||||
{% if segment.segment_type == "title" %}
|
||||
<h1>{{ segment.content }}</h1>
|
||||
{% elif segment.segment_type == "subtitle" %}
|
||||
<h2>{{ segment.content }}</h2>
|
||||
{% elif segment.segment_type == "h1" %}
|
||||
<h1>{{ segment.content }}</h1>
|
||||
{% elif segment.segment_type == "h2" %}
|
||||
<h2>{{ segment.content }}</h2>
|
||||
{% elif segment.segment_type == "h3" %}
|
||||
<h3>{{ segment.content }}</h3>
|
||||
{% elif segment.segment_type == "body" %}
|
||||
<p>{{ segment.content|safe }}</p>
|
||||
{% elif segment.segment_type == "quote" %}
|
||||
<blockquote>{{ segment.content }}</blockquote>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
@@ -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/<uuid:document_id>/', v.document, name='document'),
|
||||
path('preview/<str:name>/', v.template_preview, name='template_preview'),
|
||||
]
|
||||
|
||||
@@ -2,10 +2,11 @@ import logging
|
||||
|
||||
from django.shortcuts import render, redirect , get_object_or_404
|
||||
from .forms import OrganizationForm
|
||||
from .models import Organization,Document,Risk
|
||||
from .models import Organization,Document,Risk, DocumentTemplate
|
||||
from backend.core.utils import get_top_risk
|
||||
from django.urls import reverse
|
||||
from backend.accounts.utils import send_confirmation_email
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
|
||||
# @login_required
|
||||
# def index(request):
|
||||
@@ -73,3 +74,9 @@ def document(request, document_id):
|
||||
'organization': doc.organization,
|
||||
'segments': doc.segments.all(),
|
||||
})
|
||||
|
||||
@staff_member_required
|
||||
def template_preview(request, name):
|
||||
template = get_object_or_404(DocumentTemplate, name=name)
|
||||
parsed_template = template.to_dict()
|
||||
return render(request, 'template_preview.html', {'template': parsed_template})
|
||||
@@ -140,9 +140,6 @@ EMAIL_USE_TLS = True
|
||||
EMAIL_HOST_USER = os.getenv("CONF_MAIL")
|
||||
EMAIL_HOST_PASSWORD = os.getenv("CONF_MAIL_PASSWORD")
|
||||
|
||||
print(f"Email: {EMAIL_HOST_USER}")
|
||||
print(f"Password: {EMAIL_HOST_PASSWORD}")
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
37
document_template.yml
Normal file
37
document_template.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
- segment_type: "title"
|
||||
content: "Document Title"
|
||||
|
||||
- segment_type: "subtitle"
|
||||
content: "Document Subtitle"
|
||||
|
||||
- segment_type: "h1"
|
||||
content: "Introduction"
|
||||
|
||||
- segment_type: "body"
|
||||
content: "{{ dynamic_intro }}"
|
||||
|
||||
- segment_type: "h2"
|
||||
content: "Section 1: Details"
|
||||
|
||||
- segment_type: "body"
|
||||
content: |
|
||||
<p>This is a static section with an embedded HTML table:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Data 1</td>
|
||||
<td>Data 2</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
- segment_type: "quote"
|
||||
content: "{{ dynamic_quote }}"
|
||||
|
||||
- segment_type: "h3"
|
||||
content: "Subsection 1.1"
|
||||
|
||||
- segment_type: "body"
|
||||
content: "{{ dynamic_subsection }}"
|
||||
Reference in New Issue
Block a user