Explorar o código

Primer commit

Celestino Rey hai 5 meses
achega
880fd4b590

BIN=BIN
db.sqlite3


+ 0 - 0
doc_generator/__init__.py


+ 16 - 0
doc_generator/asgi.py

@@ -0,0 +1,16 @@
+"""
+ASGI config for doc_generator project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'doc_generator.settings')
+
+application = get_asgi_application()

+ 124 - 0
doc_generator/settings.py

@@ -0,0 +1,124 @@
+"""
+Django settings for doc_generator project.
+
+Generated by 'django-admin startproject' using Django 4.2.29.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/4.2/ref/settings/
+"""
+
+from pathlib import Path
+
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'django-insecure-qb$!mc5&7qcph*kb&j@x$$zce*i9-3es0%9lw$x89n1nl6ncjy'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+    'textdocs',
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'doc_generator.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [BASE_DIR / 'templates'],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
+WSGI_APPLICATION = 'doc_generator.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.sqlite3',
+        'NAME': BASE_DIR / 'db.sqlite3',
+    }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/4.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/4.2/howto/static-files/
+
+STATIC_URL = 'static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

+ 23 - 0
doc_generator/urls.py

@@ -0,0 +1,23 @@
+"""
+URL configuration for doc_generator project.
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/4.2/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path, include
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+    path('', include('textdocs.urls')),
+]

+ 16 - 0
doc_generator/wsgi.py

@@ -0,0 +1,16 @@
+"""
+WSGI config for doc_generator project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'doc_generator.settings')
+
+application = get_wsgi_application()

+ 22 - 0
manage.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+    """Run administrative tasks."""
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'doc_generator.settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+    main()

+ 17 - 0
requirements.txt

@@ -0,0 +1,17 @@
+asgiref==3.11.1
+brotli==1.2.0
+cffi==2.0.0
+cssselect2==0.8.0
+Django==4.2.29
+fonttools==4.60.2
+pillow==11.3.0
+pycparser==2.23
+pydyf==0.11.0
+pyphen==0.17.2
+sqlparse==0.5.5
+tinycss2==1.4.0
+tinyhtml5==2.0.0
+typing_extensions==4.15.0
+weasyprint==66.0
+webencodings==0.5.1
+zopfli==0.2.3.post1

+ 13 - 0
templates/textdocs/base.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>{% block title %}Document Generator{% endblock %}</title>
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
+</head>
+<body>
+    <div class="container mt-4">
+        <h1 class="mb-4"><a href="{% url 'document_list' %}" class="text-dark text-decoration-none">Document Generator</a></h1>
+        {% block content %}{% endblock %}
+    </div>
+</body>
+</html>

+ 19 - 0
templates/textdocs/document_detail.html

@@ -0,0 +1,19 @@
+{% extends 'textdocs/base.html' %}
+{% block content %}
+    <div class="card">
+        <div class="card-header d-flex justify-content-between align-items-center">
+            <h2>{{ document.title }}</h2>
+            <a href="{% url 'generate_pdf' pk=document.pk %}" class="btn btn-info">Export to PDF</a>
+        </div>
+        <div class="card-body">
+            <h4 class="card-title">Introduction</h4>
+            <p class="card-text">{{ document.introduction|linebreaks }}</p>
+            <hr>
+            <h4 class="card-title">Main Body</h4>
+            <p class="card-text">{{ document.main_body|linebreaks }}</p>
+            <hr>
+            <h4 class="card-title">Conclusion</h4>
+            <p class="card-text">{{ document.conclusion|linebreaks }}</p>
+        </div>
+    </div>
+{% endblock %}

+ 9 - 0
templates/textdocs/document_form.html

@@ -0,0 +1,9 @@
+{% extends 'textdocs/base.html' %}
+{% block content %}
+    <h2>Create a New Document</h2>
+    <form method="post">
+        {% csrf_token %}
+        {{ form.as_p }}
+        <button type="submit" class="btn btn-success">Save Document</button>
+    </form>
+{% endblock %}

+ 14 - 0
templates/textdocs/document_list.hml

@@ -0,0 +1,14 @@
+{% extends 'textdocs/base.html' %}
+{% block content %}
+    <a href="{% url 'document_create' %}" class="btn btn-primary mb-3">Create New Document</a>
+    <div class="list-group">
+    {% for doc in documents %}
+        <a href="{{ doc.get_absolute_url }}" class="list-group-item list-group-item-action">
+            <h5 class="mb-1">{{ doc.title }}</h5>
+            <small>Created on: {{ doc.created_at|date:"F j, Y" }}</small>
+        </a>
+    {% empty %}
+        <p>No documents found. Create your first one!</p>
+    {% endfor %}
+    </div>
+{% endblock %}

+ 53 - 0
templates/textdocs/document_pdf.html

@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>{{ document.title }}</title>
+    <style>
+        @page {
+            size: A4;
+            margin: 2cm;
+            /* Adds content to the bottom-right of each page */
+            @bottom-right {
+                content: "Page " counter(page) " of " counter(pages);
+                font-size: 10pt;
+            }
+        }
+        body {
+            font-family: "Helvetica", sans-serif;
+            font-size: 12pt;
+            line-height: 1.6;
+        }
+        h1, h2, h3, h4 {
+            font-family: "Times New Roman", serif;
+            color: #005595; /* Siemens-like blue */
+        }
+        h1 {
+            font-size: 28pt;
+            text-align: center;
+            margin-bottom: 2cm;
+        }
+        h2 {
+            font-size: 18pt;
+            border-bottom: 2px solid #009999; /* Siemens-like teal */
+            padding-bottom: 5px;
+            margin-top: 1.5cm;
+        }
+        p {
+            text-align: justify;
+        }
+    </style>
+</head>
+<body>
+    <h1>{{ document.title }}</h1>
+
+    <h2>Introduction</h2>
+    <p>{{ document.introduction|linebreaks }}</p>
+
+    <h2>Main Body</h2>
+    <p>{{ document.main_body|linebreaks }}</p>
+
+    <h2>Conclusion</h2>
+    <p>{{ document.conclusion|linebreaks }}</p>
+</body>
+</html>

+ 0 - 0
textdocs/__init__.py


+ 3 - 0
textdocs/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 6 - 0
textdocs/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class TextdocsConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'textdocs'

+ 14 - 0
textdocs/forms.py

@@ -0,0 +1,14 @@
+# textdocs/forms.py
+from django import forms
+from .models import Document
+
+class DocumentForm(forms.ModelForm):
+    class Meta:
+        model = Document
+        fields = ['title', 'introduction', 'main_body', 'conclusion']
+        widgets = {
+            'title': forms.TextInput(attrs={'class': 'form-control'}),
+            'introduction': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
+            'main_body': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
+            'conclusion': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
+        }

+ 25 - 0
textdocs/migrations/0001_initial.py

@@ -0,0 +1,25 @@
+# Generated by Django 4.2.29 on 2026-03-05 11:58
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Document',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=255)),
+                ('introduction', models.TextField(help_text='The introductory section of the document.')),
+                ('main_body', models.TextField(help_text='The main content or analysis.')),
+                ('conclusion', models.TextField(help_text='The concluding remarks or summary.')),
+                ('created_at', models.DateTimeField(auto_now_add=True)),
+            ],
+        ),
+    ]

+ 0 - 0
textdocs/migrations/__init__.py


+ 17 - 0
textdocs/models.py

@@ -0,0 +1,17 @@
+# textdocs/models.py
+from django.db import models
+from django.urls import reverse
+
+class Document(models.Model):
+    """Represents a single document with its fixed sections."""
+    title = models.CharField(max_length=255)
+    introduction = models.TextField(help_text="The introductory section of the document.")
+    main_body = models.TextField(help_text="The main content or analysis.")
+    conclusion = models.TextField(help_text="The concluding remarks or summary.")
+    created_at = models.DateTimeField(auto_now_add=True)
+
+    def __str__(self):
+        return self.title
+
+    def get_absolute_url(self):
+        return reverse('document_detail', kwargs={'pk': self.pk})

+ 3 - 0
textdocs/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 10 - 0
textdocs/urls.py

@@ -0,0 +1,10 @@
+# textdocs/urls.py
+from django.urls import path
+from . import views
+
+urlpatterns = [
+    path('', views.document_list, name='document_list'),
+    path('document/new/', views.document_create, name='document_create'),
+    path('document/<int:pk>/', views.document_detail, name='document_detail'),
+    path('document/<int:pk>/pdf/', views.generate_pdf, name='generate_pdf'),
+]

+ 45 - 0
textdocs/views.py

@@ -0,0 +1,45 @@
+# textdocs/views.py
+from django.shortcuts import render, get_object_or_404, redirect
+from django.http import HttpResponse
+from django.template.loader import render_to_string
+from weasyprint import HTML
+
+from .models import Document
+from .forms import DocumentForm
+
+def document_list(request):
+    """Shows a list of all created documents."""
+    documents = Document.objects.order_by('-created_at')
+    return render(request, 'textdocs/document_list.html', {'documents': documents})
+
+def document_detail(request, pk):
+    """Shows the details of a single document."""
+    document = get_object_or_404(Document, pk=pk)
+    return render(request, 'textdocs/document_detail.html', {'document': document})
+
+def document_create(request):
+    """Handles the creation of a new document via a form."""
+    if request.method == 'POST':
+        form = DocumentForm(request.POST)
+        if form.is_valid():
+            document = form.save()
+            return redirect('document_detail', pk=document.pk)
+    else:
+        form = DocumentForm()
+    return render(request, 'textdocs/document_form.html', {'form': form})
+
+def generate_pdf(request, pk):
+    """Generates a PDF for a specific document."""
+    document = get_object_or_404(Document, pk=pk)
+
+    # Render the HTML template with the document context
+    html_string = render_to_string('textdocs/document_pdf.html', {'document': document})
+
+    # Use WeasyPrint to create the PDF
+    html = HTML(string=html_string, base_url=request.build_absolute_uri())
+    pdf = html.write_pdf()
+
+    # Create an HTTP response with the PDF
+    response = HttpResponse(pdf, content_type='application/pdf')
+    response['Content-Disposition'] = f'attachment; filename="{document.title}.pdf"'
+    return response