Files
AccountForger-neuerUpload/utils/profile_export_service.py
Claude Project Manager 404100abc4 JSON statt txt
2025-11-16 23:06:33 +01:00

427 Zeilen
14 KiB
Python

"""
Profil-Export-Service für Account-Daten
Exportiert Account-Profile in verschiedene Formate (CSV, JSON, PDF).
"""
import os
import csv
import json
import string
import logging
from io import BytesIO, StringIO
from datetime import datetime
from typing import Dict, Any, List
from pathlib import Path
logger = logging.getLogger("profile_export_service")
class ProfileExportService:
"""Service für den Export von Account-Profilen"""
# Felder die exportiert werden (ohne id, last_login, notes, status)
EXPORT_FIELDS = {
"username": "Username",
"password": "Passwort",
"email": "E-Mail",
"phone": "Telefon",
"platform": "Plattform",
"full_name": "Name",
"created_at": "Erstellt_am"
}
@staticmethod
def export_to_csv(account_data: Dict[str, Any]) -> bytes:
"""
Exportiert Account-Daten als CSV.
Args:
account_data: Dictionary mit Account-Daten
Returns:
CSV-Daten als bytes
"""
try:
output = StringIO()
writer = csv.DictWriter(
output,
fieldnames=ProfileExportService.EXPORT_FIELDS.values(),
quoting=csv.QUOTE_MINIMAL
)
# Header schreiben
writer.writeheader()
# Datenzeile vorbereiten
row_data = {}
for field_key, field_label in ProfileExportService.EXPORT_FIELDS.items():
value = account_data.get(field_key, "")
# None zu leerem String konvertieren
row_data[field_label] = value if value is not None else ""
# Datenzeile schreiben
writer.writerow(row_data)
# In bytes konvertieren (UTF-8)
csv_content = output.getvalue()
output.close()
logger.info("CSV-Export erfolgreich")
return csv_content.encode('utf-8')
except Exception as e:
logger.error(f"Fehler beim CSV-Export: {e}")
raise
@staticmethod
def export_to_txt(account_data: Dict[str, Any]) -> bytes:
"""
Exportiert Account-Daten als TXT (Key-Value Format).
Args:
account_data: Dictionary mit Account-Daten
Returns:
TXT-Daten als bytes
"""
try:
lines = []
for field_key, field_label in ProfileExportService.EXPORT_FIELDS.items():
value = account_data.get(field_key, "")
# None zu leerem String konvertieren
value = value if value is not None else ""
lines.append(f"{field_label}: {value}")
# Exportdatum hinzufügen
export_date = datetime.now().strftime("%d.%m.%Y %H:%M")
lines.append(f"\nExportiert am: {export_date}")
txt_content = "\n".join(lines)
logger.info("TXT-Export erfolgreich")
return txt_content.encode('utf-8')
except Exception as e:
logger.error(f"Fehler beim TXT-Export: {e}")
raise
@staticmethod
def export_to_json(account_data: Dict[str, Any]) -> bytes:
"""
Exportiert Account-Daten als JSON.
Args:
account_data: Dictionary mit Account-Daten
Returns:
JSON-Daten als bytes
"""
try:
# JSON-Struktur aufbauen
export_data = {
"account": {
field_key: account_data.get(field_key, "")
for field_key in ProfileExportService.EXPORT_FIELDS.keys()
},
"export_info": {
"exported_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"format_version": "1.0"
}
}
# None-Werte zu leeren Strings konvertieren
for key, value in export_data["account"].items():
if value is None:
export_data["account"][key] = ""
# JSON mit Einrückung für Lesbarkeit
json_content = json.dumps(
export_data,
ensure_ascii=False,
indent=2,
sort_keys=False
)
logger.info("JSON-Export erfolgreich")
return json_content.encode('utf-8')
except Exception as e:
logger.error(f"Fehler beim JSON-Export: {e}")
raise
@staticmethod
def export_to_pdf(account_data: Dict[str, Any]) -> bytes:
"""
Exportiert Account-Daten als PDF mit schönem Layout.
Args:
account_data: Dictionary mit Account-Daten
Returns:
PDF-Daten als bytes
"""
try:
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
# PDF-Buffer erstellen
buffer = BytesIO()
# Dokument erstellen
doc = SimpleDocTemplate(
buffer,
pagesize=A4,
rightMargin=20*mm,
leftMargin=20*mm,
topMargin=20*mm,
bottomMargin=20*mm
)
# Story (Inhalt) erstellen
story = []
styles = getSampleStyleSheet()
# Custom Styles
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Title'],
fontSize=20,
textColor=colors.HexColor('#1F2937'),
spaceAfter=5*mm,
alignment=TA_CENTER
)
heading_style = ParagraphStyle(
'CustomHeading',
parent=styles['Heading2'],
fontSize=14,
textColor=colors.HexColor('#374151'),
spaceAfter=3*mm,
spaceBefore=5*mm
)
# IntelSight Logo versuchen zu laden
logo_path = Path("resources/icons/intelsight-logo.svg")
if logo_path.exists():
try:
# SVG zu reportlab Image (mit svglib falls verfügbar)
try:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF
drawing = svg2rlg(str(logo_path))
if drawing:
# Skalieren auf vernünftige Größe
drawing.width = 40*mm
drawing.height = 10*mm
drawing.scale(40*mm/drawing.width, 10*mm/drawing.height)
story.append(drawing)
story.append(Spacer(1, 5*mm))
except ImportError:
# svglib nicht verfügbar - überspringen
logger.debug("svglib nicht verfügbar - Logo wird übersprungen")
except Exception as e:
logger.debug(f"Logo konnte nicht geladen werden: {e}")
# Titel
username = account_data.get("username", "Unbekannt")
platform = account_data.get("platform", "Unbekannt")
title = Paragraph(f"Account-Profil: {username}", title_style)
story.append(title)
# Plattform
platform_text = Paragraph(f"Plattform: {platform.title()}", styles['Normal'])
story.append(platform_text)
story.append(Spacer(1, 10*mm))
# LOGIN-DATEN Sektion
login_heading = Paragraph("LOGIN-DATEN", heading_style)
story.append(login_heading)
# Login-Daten Tabelle
login_data = [
["Benutzername:", account_data.get("username", "")],
["Passwort:", account_data.get("password", "")],
["E-Mail:", account_data.get("email", "") or "-"],
["Telefon:", account_data.get("phone", "") or "-"]
]
login_table = Table(login_data, colWidths=[45*mm, 115*mm])
login_table.setStyle(TableStyle([
('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
('FONTNAME', (1, 0), (1, -1), 'Helvetica'),
('FONTSIZE', (0, 0), (-1, -1), 11),
('TEXTCOLOR', (0, 0), (0, -1), colors.HexColor('#6B7280')),
('TEXTCOLOR', (1, 0), (1, -1), colors.HexColor('#1F2937')),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0),
('TOPPADDING', (0, 0), (-1, -1), 2*mm),
('BOTTOMPADDING', (0, 0), (-1, -1), 2*mm),
]))
story.append(login_table)
story.append(Spacer(1, 8*mm))
# PROFIL-INFORMATIONEN Sektion
profile_heading = Paragraph("PROFIL-INFORMATIONEN", heading_style)
story.append(profile_heading)
# Profil-Daten Tabelle
profile_data = [
["Name:", account_data.get("full_name", "") or "-"],
["Erstellt am:", account_data.get("created_at", "") or "-"]
]
profile_table = Table(profile_data, colWidths=[45*mm, 115*mm])
profile_table.setStyle(TableStyle([
('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'),
('FONTNAME', (1, 0), (1, -1), 'Helvetica'),
('FONTSIZE', (0, 0), (-1, -1), 11),
('TEXTCOLOR', (0, 0), (0, -1), colors.HexColor('#6B7280')),
('TEXTCOLOR', (1, 0), (1, -1), colors.HexColor('#1F2937')),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0),
('TOPPADDING', (0, 0), (-1, -1), 2*mm),
('BOTTOMPADDING', (0, 0), (-1, -1), 2*mm),
]))
story.append(profile_table)
story.append(Spacer(1, 15*mm))
# Export-Datum (Footer)
export_date = datetime.now().strftime("%d.%m.%Y %H:%M")
footer_text = Paragraph(
f"Exportiert am: {export_date}",
ParagraphStyle(
'Footer',
parent=styles['Normal'],
fontSize=9,
textColor=colors.HexColor('#9CA3AF'),
alignment=TA_CENTER
)
)
story.append(footer_text)
# PDF erstellen
doc.build(story)
# Buffer-Wert holen
pdf_content = buffer.getvalue()
buffer.close()
logger.info("PDF-Export erfolgreich")
return pdf_content
except ImportError as e:
logger.error(f"reportlab nicht installiert: {e}")
raise Exception(
"PDF-Export erfordert 'reportlab' Library. "
"Bitte installieren Sie: pip install reportlab"
)
except Exception as e:
logger.error(f"Fehler beim PDF-Export: {e}")
raise
@staticmethod
def generate_filename(
account_data: Dict[str, Any],
format_extension: str,
include_timestamp: bool = True
) -> str:
"""
Generiert einen Dateinamen nach Konvention.
Format: {username}_{platform}_{timestamp}.{extension}
Beispiel: max_mueller_instagram_2025-11-10_14-30.csv
Args:
account_data: Account-Daten
format_extension: Dateiendung (z.B. "csv", "txt", "pdf", "zip")
include_timestamp: Ob Timestamp hinzugefügt werden soll
Returns:
Generierter Dateiname
"""
# Username und Platform aus Account-Daten
username = account_data.get("username", "account")
platform = account_data.get("platform", "unknown").lower()
# Sonderzeichen entfernen für Dateinamen
username = ProfileExportService._sanitize_filename(username)
platform = ProfileExportService._sanitize_filename(platform)
# Timestamp
if include_timestamp:
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
filename = f"{username}_{platform}_{timestamp}.{format_extension}"
else:
filename = f"{username}_{platform}.{format_extension}"
return filename
@staticmethod
def _sanitize_filename(filename: str) -> str:
"""
Entfernt ungültige Zeichen aus Dateinamen.
Args:
filename: Ursprünglicher Dateiname
Returns:
Bereinigter Dateiname
"""
# Nur alphanumerische Zeichen, Unterstrich und Bindestrich erlauben
valid_chars = f"-_.{string.ascii_letters}{string.digits}"
sanitized = ''.join(c if c in valid_chars else '_' for c in filename)
return sanitized
@staticmethod
def export_account(
account_data: Dict[str, Any],
formats: List[str],
password_protect: bool = False
) -> Dict[str, bytes]:
"""
Exportiert Account-Daten in angegebene Formate.
Args:
account_data: Account-Daten zum Exportieren
formats: Liste von Formaten ["csv", "json", "pdf"]
password_protect: Wird ignoriert (für Rückwärtskompatibilität)
Returns:
Dict mit {filename: content} für alle Formate
"""
files_dict = {}
# Jedes Format exportieren
for fmt in formats:
fmt = fmt.lower()
if fmt == "csv":
content = ProfileExportService.export_to_csv(account_data)
filename = ProfileExportService.generate_filename(account_data, "csv")
files_dict[filename] = content
elif fmt == "json":
content = ProfileExportService.export_to_json(account_data)
filename = ProfileExportService.generate_filename(account_data, "json")
files_dict[filename] = content
elif fmt == "pdf":
content = ProfileExportService.export_to_pdf(account_data)
filename = ProfileExportService.generate_filename(account_data, "pdf")
files_dict[filename] = content
else:
logger.warning(f"Unbekanntes Format ignoriert: {fmt}")
return files_dict