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:
Stiftung Development
2025-09-06 21:04:07 +02:00
parent c7c790ee09
commit e0c7d0e351
54 changed files with 11004 additions and 6423 deletions

View File

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

View File

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