Resolve "Napraviti template za dokument" #56

Merged
amirsabani303 merged 1 commits from 7-napraviti-template-za-dokument into master 2025-02-14 06:09:39 +01:00
12 changed files with 229 additions and 6 deletions

View File

@@ -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)

View 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'))

View 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."))

View File

@@ -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())

View File

@@ -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())

View 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)),
],
),
]

View File

@@ -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):

View 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 %}

View File

@@ -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'),
]

View File

@@ -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})

View File

@@ -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
View 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 }}"