Format code with Black and isort for CI/CD compliance
- Apply Black formatting to all Python files in core and stiftung modules - Fix import statement ordering with isort - Ensure all code meets automated quality standards - Resolve CI/CD pipeline formatting failures - Maintain consistent code style across the entire codebase
This commit is contained in:
@@ -1,61 +1,65 @@
|
||||
"""
|
||||
Configuration utilities for the Stiftung application
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from stiftung.models import AppConfiguration
|
||||
|
||||
|
||||
def get_config(key, default=None, fallback_to_settings=True):
|
||||
"""
|
||||
Get a configuration value from the database or fall back to Django settings
|
||||
|
||||
|
||||
Args:
|
||||
key: The configuration key
|
||||
default: Default value if not found
|
||||
fallback_to_settings: If True, try to get from Django settings using the key in uppercase
|
||||
|
||||
|
||||
Returns:
|
||||
The configuration value
|
||||
"""
|
||||
# Try to get from AppConfiguration first
|
||||
value = AppConfiguration.get_setting(key, None)
|
||||
|
||||
|
||||
# Fall back to Django settings if value is None or empty string
|
||||
if not value and fallback_to_settings:
|
||||
settings_key = key.upper()
|
||||
return getattr(settings, settings_key, default)
|
||||
|
||||
|
||||
return value if value is not None else default
|
||||
|
||||
|
||||
def get_paperless_config():
|
||||
"""
|
||||
Get all Paperless-related configuration values
|
||||
|
||||
|
||||
Returns:
|
||||
dict: Dictionary containing all Paperless configuration
|
||||
"""
|
||||
return {
|
||||
'api_url': get_config('paperless_api_url', 'http://192.168.178.167:30070'),
|
||||
'api_token': get_config('paperless_api_token', ''),
|
||||
'destinataere_tag': get_config('paperless_destinataere_tag', 'Stiftung_Destinatäre'),
|
||||
'destinataere_tag_id': get_config('paperless_destinataere_tag_id', '210'),
|
||||
'land_tag': get_config('paperless_land_tag', 'Stiftung_Land_und_Pächter'),
|
||||
'land_tag_id': get_config('paperless_land_tag_id', '204'),
|
||||
'admin_tag': get_config('paperless_admin_tag', 'Stiftung_Administration'),
|
||||
'admin_tag_id': get_config('paperless_admin_tag_id', '216'),
|
||||
"api_url": get_config("paperless_api_url", "http://192.168.178.167:30070"),
|
||||
"api_token": get_config("paperless_api_token", ""),
|
||||
"destinataere_tag": get_config(
|
||||
"paperless_destinataere_tag", "Stiftung_Destinatäre"
|
||||
),
|
||||
"destinataere_tag_id": get_config("paperless_destinataere_tag_id", "210"),
|
||||
"land_tag": get_config("paperless_land_tag", "Stiftung_Land_und_Pächter"),
|
||||
"land_tag_id": get_config("paperless_land_tag_id", "204"),
|
||||
"admin_tag": get_config("paperless_admin_tag", "Stiftung_Administration"),
|
||||
"admin_tag_id": get_config("paperless_admin_tag_id", "216"),
|
||||
}
|
||||
|
||||
|
||||
def set_config(key, value, **kwargs):
|
||||
"""
|
||||
Set a configuration value
|
||||
|
||||
|
||||
Args:
|
||||
key: The configuration key
|
||||
value: The value to set
|
||||
**kwargs: Additional parameters for AppConfiguration.set_setting
|
||||
|
||||
|
||||
Returns:
|
||||
AppConfiguration: The configuration object
|
||||
"""
|
||||
@@ -65,9 +69,9 @@ def set_config(key, value, **kwargs):
|
||||
def is_paperless_configured():
|
||||
"""
|
||||
Check if Paperless is properly configured
|
||||
|
||||
|
||||
Returns:
|
||||
bool: True if API URL and token are configured
|
||||
"""
|
||||
config = get_paperless_config()
|
||||
return bool(config['api_url'] and config['api_token'])
|
||||
return bool(config["api_url"] and config["api_token"])
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
"""
|
||||
PDF generation utilities with corporate identity support
|
||||
"""
|
||||
import os
|
||||
|
||||
import base64
|
||||
import os
|
||||
from io import BytesIO
|
||||
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
from django.http import HttpResponse
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
|
||||
# Try to import WeasyPrint, fall back gracefully if not available
|
||||
try:
|
||||
from weasyprint import HTML, CSS
|
||||
from weasyprint import CSS, HTML
|
||||
from weasyprint.text.fonts import FontConfiguration
|
||||
|
||||
WEASYPRINT_AVAILABLE = True
|
||||
IMPORT_ERROR = None
|
||||
except ImportError as e:
|
||||
@@ -35,72 +38,84 @@ from stiftung.models import AppConfiguration
|
||||
|
||||
class PDFGenerator:
|
||||
"""Corporate identity PDF generator"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
if WEASYPRINT_AVAILABLE:
|
||||
self.font_config = FontConfiguration()
|
||||
else:
|
||||
self.font_config = None
|
||||
|
||||
|
||||
def is_available(self):
|
||||
"""Check if PDF generation is available"""
|
||||
return WEASYPRINT_AVAILABLE
|
||||
|
||||
|
||||
def get_corporate_settings(self):
|
||||
"""Get corporate identity settings from configuration"""
|
||||
return {
|
||||
'stiftung_name': AppConfiguration.get_setting('corporate_stiftung_name', 'Stiftung'),
|
||||
'logo_path': AppConfiguration.get_setting('corporate_logo_path', ''),
|
||||
'primary_color': AppConfiguration.get_setting('corporate_primary_color', '#2c3e50'),
|
||||
'secondary_color': AppConfiguration.get_setting('corporate_secondary_color', '#3498db'),
|
||||
'address_line1': AppConfiguration.get_setting('corporate_address_line1', ''),
|
||||
'address_line2': AppConfiguration.get_setting('corporate_address_line2', ''),
|
||||
'phone': AppConfiguration.get_setting('corporate_phone', ''),
|
||||
'email': AppConfiguration.get_setting('corporate_email', ''),
|
||||
'website': AppConfiguration.get_setting('corporate_website', ''),
|
||||
'footer_text': AppConfiguration.get_setting('corporate_footer_text', 'Dieser Bericht wurde automatisch generiert.'),
|
||||
"stiftung_name": AppConfiguration.get_setting(
|
||||
"corporate_stiftung_name", "Stiftung"
|
||||
),
|
||||
"logo_path": AppConfiguration.get_setting("corporate_logo_path", ""),
|
||||
"primary_color": AppConfiguration.get_setting(
|
||||
"corporate_primary_color", "#2c3e50"
|
||||
),
|
||||
"secondary_color": AppConfiguration.get_setting(
|
||||
"corporate_secondary_color", "#3498db"
|
||||
),
|
||||
"address_line1": AppConfiguration.get_setting(
|
||||
"corporate_address_line1", ""
|
||||
),
|
||||
"address_line2": AppConfiguration.get_setting(
|
||||
"corporate_address_line2", ""
|
||||
),
|
||||
"phone": AppConfiguration.get_setting("corporate_phone", ""),
|
||||
"email": AppConfiguration.get_setting("corporate_email", ""),
|
||||
"website": AppConfiguration.get_setting("corporate_website", ""),
|
||||
"footer_text": AppConfiguration.get_setting(
|
||||
"corporate_footer_text", "Dieser Bericht wurde automatisch generiert."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def get_logo_base64(self, logo_path):
|
||||
"""Convert logo to base64 for embedding in PDF"""
|
||||
if not logo_path:
|
||||
return None
|
||||
|
||||
|
||||
# Try different possible paths
|
||||
possible_paths = [
|
||||
logo_path,
|
||||
os.path.join(settings.MEDIA_ROOT, logo_path),
|
||||
os.path.join(settings.STATIC_ROOT or '', logo_path),
|
||||
os.path.join(settings.BASE_DIR, 'static', logo_path),
|
||||
os.path.join(settings.STATIC_ROOT or "", logo_path),
|
||||
os.path.join(settings.BASE_DIR, "static", logo_path),
|
||||
]
|
||||
|
||||
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
with open(path, 'rb') as img_file:
|
||||
img_data = base64.b64encode(img_file.read()).decode('utf-8')
|
||||
with open(path, "rb") as img_file:
|
||||
img_data = base64.b64encode(img_file.read()).decode("utf-8")
|
||||
# Determine MIME type
|
||||
ext = os.path.splitext(path)[1].lower()
|
||||
if ext in ['.jpg', '.jpeg']:
|
||||
mime_type = 'image/jpeg'
|
||||
elif ext == '.png':
|
||||
mime_type = 'image/png'
|
||||
elif ext == '.svg':
|
||||
mime_type = 'image/svg+xml'
|
||||
if ext in [".jpg", ".jpeg"]:
|
||||
mime_type = "image/jpeg"
|
||||
elif ext == ".png":
|
||||
mime_type = "image/png"
|
||||
elif ext == ".svg":
|
||||
mime_type = "image/svg+xml"
|
||||
else:
|
||||
mime_type = 'image/png' # default
|
||||
|
||||
mime_type = "image/png" # default
|
||||
|
||||
return f"data:{mime_type};base64,{img_data}"
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_base_css(self, corporate_settings):
|
||||
"""Generate base CSS for corporate identity"""
|
||||
primary_color = corporate_settings.get('primary_color', '#2c3e50')
|
||||
secondary_color = corporate_settings.get('secondary_color', '#3498db')
|
||||
|
||||
primary_color = corporate_settings.get("primary_color", "#2c3e50")
|
||||
secondary_color = corporate_settings.get("secondary_color", "#3498db")
|
||||
|
||||
return f"""
|
||||
@page {{
|
||||
size: A4;
|
||||
@@ -291,7 +306,7 @@ class PDFGenerator:
|
||||
page-break-before: always;
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def generate_pdf_response(self, html_content, filename, css_content=None):
|
||||
"""Generate PDF response from HTML content"""
|
||||
if not WEASYPRINT_AVAILABLE:
|
||||
@@ -320,27 +335,30 @@ class PDFGenerator:
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
response = HttpResponse(error_html, content_type='text/html')
|
||||
response['Content-Disposition'] = f'inline; filename="{filename.replace(".pdf", "_preview.html")}"'
|
||||
response = HttpResponse(error_html, content_type="text/html")
|
||||
response["Content-Disposition"] = (
|
||||
f'inline; filename="{filename.replace(".pdf", "_preview.html")}"'
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
try:
|
||||
# Create CSS string
|
||||
if css_content:
|
||||
css = CSS(string=css_content, font_config=self.font_config)
|
||||
else:
|
||||
css = None
|
||||
|
||||
|
||||
# Generate PDF
|
||||
html_doc = HTML(string=html_content)
|
||||
pdf_bytes = html_doc.write_pdf(stylesheets=[css] if css else None,
|
||||
font_config=self.font_config)
|
||||
|
||||
pdf_bytes = html_doc.write_pdf(
|
||||
stylesheets=[css] if css else None, font_config=self.font_config
|
||||
)
|
||||
|
||||
# Create response
|
||||
response = HttpResponse(pdf_bytes, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||
response = HttpResponse(pdf_bytes, content_type="application/pdf")
|
||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
return response
|
||||
|
||||
|
||||
except Exception as e:
|
||||
# Fallback: return error message as HTML
|
||||
error_html = f"""
|
||||
@@ -368,15 +386,19 @@ class PDFGenerator:
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
response = HttpResponse(error_html, content_type='text/html')
|
||||
response['Content-Disposition'] = f'inline; filename="error_{filename.replace(".pdf", ".html")}"'
|
||||
|
||||
response = HttpResponse(error_html, content_type="text/html")
|
||||
response["Content-Disposition"] = (
|
||||
f'inline; filename="error_{filename.replace(".pdf", ".html")}"'
|
||||
)
|
||||
return response
|
||||
|
||||
def export_data_list_pdf(self, data, fields_config, title, filename_prefix, request_user=None):
|
||||
|
||||
def export_data_list_pdf(
|
||||
self, data, fields_config, title, filename_prefix, request_user=None
|
||||
):
|
||||
"""
|
||||
Export a list of data as formatted PDF
|
||||
|
||||
|
||||
Args:
|
||||
data: QuerySet or list of model instances
|
||||
fields_config: dict with field names as keys and display names as values
|
||||
@@ -385,34 +407,39 @@ class PDFGenerator:
|
||||
request_user: User making the request (for audit purposes)
|
||||
"""
|
||||
corporate_settings = self.get_corporate_settings()
|
||||
logo_base64 = self.get_logo_base64(corporate_settings.get('logo_path', ''))
|
||||
|
||||
logo_base64 = self.get_logo_base64(corporate_settings.get("logo_path", ""))
|
||||
|
||||
# Prepare context
|
||||
context = {
|
||||
'corporate_settings': corporate_settings,
|
||||
'logo_base64': logo_base64,
|
||||
'title': title,
|
||||
'data': data,
|
||||
'fields_config': fields_config,
|
||||
'generation_date': timezone.now(),
|
||||
'generated_by': (request_user.get_full_name()
|
||||
if hasattr(request_user, 'get_full_name') and request_user.get_full_name()
|
||||
else request_user.username
|
||||
if hasattr(request_user, 'username') and request_user.username
|
||||
else 'System'),
|
||||
'total_count': len(data) if hasattr(data, '__len__') else data.count(),
|
||||
"corporate_settings": corporate_settings,
|
||||
"logo_base64": logo_base64,
|
||||
"title": title,
|
||||
"data": data,
|
||||
"fields_config": fields_config,
|
||||
"generation_date": timezone.now(),
|
||||
"generated_by": (
|
||||
request_user.get_full_name()
|
||||
if hasattr(request_user, "get_full_name")
|
||||
and request_user.get_full_name()
|
||||
else (
|
||||
request_user.username
|
||||
if hasattr(request_user, "username") and request_user.username
|
||||
else "System"
|
||||
)
|
||||
),
|
||||
"total_count": len(data) if hasattr(data, "__len__") else data.count(),
|
||||
}
|
||||
|
||||
|
||||
# Render HTML
|
||||
html_content = render_to_string('pdf/data_list.html', context)
|
||||
|
||||
html_content = render_to_string("pdf/data_list.html", context)
|
||||
|
||||
# Generate CSS
|
||||
css_content = self.get_base_css(corporate_settings)
|
||||
|
||||
|
||||
# Generate filename
|
||||
timestamp = timezone.now().strftime('%Y%m%d_%H%M%S')
|
||||
timestamp = timezone.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"{filename_prefix}_{timestamp}.pdf"
|
||||
|
||||
|
||||
return self.generate_pdf_response(html_content, filename, css_content)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user