Export Button geht jetzt

Dieser Commit ist enthalten in:
2025-06-22 18:30:11 +02:00
Ursprung 74391e6634
Commit b9b943ec15
11 geänderte Dateien mit 514 neuen und 167 gelöschten Zeilen

Datei anzeigen

@@ -241,4 +241,61 @@ def delete_note(note_id):
)
return jsonify({'success': success})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
return jsonify({'success': False, 'error': str(e)}), 500
# Export Routes
@leads_bp.route('/export')
@login_required
def export_leads():
"""Export leads data as Excel/CSV"""
from utils.export import create_excel_export, create_csv_export
try:
conn = get_db_connection()
cur = conn.cursor()
# Query institutions with contact counts
cur.execute("""
SELECT
i.id,
i.name,
i.type,
i.website,
i.address,
i.created_at,
i.created_by,
COUNT(DISTINCT c.id) as contact_count,
COUNT(DISTINCT cd.id) as contact_detail_count,
COUNT(DISTINCT n.id) as note_count
FROM lead_institutions i
LEFT JOIN lead_contacts c ON i.id = c.institution_id
LEFT JOIN lead_contact_details cd ON c.id = cd.contact_id
LEFT JOIN lead_notes n ON i.id = n.institution_id
GROUP BY i.id, i.name, i.type, i.website, i.address, i.created_at, i.created_by
ORDER BY i.name
""")
# Prepare data for export
data = []
columns = ['ID', 'Institution', 'Typ', 'Website', 'Adresse',
'Erstellt am', 'Erstellt von', 'Anzahl Kontakte',
'Anzahl Kontaktdetails', 'Anzahl Notizen']
for row in cur.fetchall():
data.append(list(row))
# Check format parameter
format_type = request.args.get('format', 'excel').lower()
if format_type == 'csv':
return create_csv_export(data, columns, 'leads')
else:
return create_excel_export(data, columns, 'leads')
except Exception as e:
flash(f'Fehler beim Export: {str(e)}', 'error')
return redirect(url_for('leads.institutions'))
finally:
cur.close()
conn.close()

Datei anzeigen

@@ -32,6 +32,14 @@
placeholder="Institution suchen..." onkeyup="filterInstitutions()">
</div>
</div>
<div class="col-md-6 text-end">
<a href="{{ url_for('leads.export_leads', format='excel') }}" class="btn btn-outline-success">
<i class="bi bi-file-excel"></i> Excel Export
</a>
<a href="{{ url_for('leads.export_leads', format='csv') }}" class="btn btn-outline-info">
<i class="bi bi-file-text"></i> CSV Export
</a>
</div>
</div>
<!-- Institutions Table -->

Datei anzeigen

@@ -5,7 +5,7 @@ from flask import Blueprint, request, send_file
import config
from auth.decorators import login_required
from utils.export import create_excel_export, prepare_audit_export_data
from utils.export import create_excel_export, create_csv_export, prepare_audit_export_data, format_datetime_for_export
from db import get_connection
# Create Blueprint
@@ -20,61 +20,32 @@ def export_licenses():
cur = conn.cursor()
try:
# Filter aus Request
show_fake = request.args.get('show_fake', 'false') == 'true'
# SQL Query mit optionalem Test-Filter
if show_fake:
query = """
SELECT
l.id,
l.license_key,
c.name as customer_name,
c.email as customer_email,
l.license_type,
l.valid_from,
l.valid_until,
l.is_active,
l.device_limit,
l.created_at,
l.is_fake,
CASE
WHEN l.valid_until < CURRENT_DATE THEN 'Abgelaufen'
WHEN l.is_active = false THEN 'Deaktiviert'
ELSE 'Aktiv'
END as status,
(SELECT COUNT(*) FROM sessions s WHERE s.license_key = l.license_key AND s.is_active = true) as active_sessions,
(SELECT COUNT(DISTINCT hardware_id) FROM sessions s WHERE s.license_key = l.license_key) as registered_devices
FROM licenses l
LEFT JOIN customers c ON l.customer_id = c.id
ORDER BY l.created_at DESC
"""
else:
query = """
SELECT
l.id,
l.license_key,
c.name as customer_name,
c.email as customer_email,
l.license_type,
l.valid_from,
l.valid_until,
l.is_active,
l.device_limit,
l.created_at,
l.is_fake,
CASE
WHEN l.valid_until < CURRENT_DATE THEN 'Abgelaufen'
WHEN l.is_active = false THEN 'Deaktiviert'
ELSE 'Aktiv'
END as status,
(SELECT COUNT(*) FROM sessions s WHERE s.license_key = l.license_key AND s.is_active = true) as active_sessions,
(SELECT COUNT(DISTINCT hardware_id) FROM sessions s WHERE s.license_key = l.license_key) as registered_devices
FROM licenses l
LEFT JOIN customers c ON l.customer_id = c.id
WHERE l.is_fake = false
ORDER BY l.created_at DESC
"""
# Nur reale Daten exportieren - keine Fake-Daten
query = """
SELECT
l.id,
l.license_key,
c.name as customer_name,
c.email as customer_email,
l.license_type,
l.valid_from,
l.valid_until,
l.is_active,
l.device_limit,
l.created_at,
l.is_fake,
CASE
WHEN l.valid_until < CURRENT_DATE THEN 'Abgelaufen'
WHEN l.is_active = false THEN 'Deaktiviert'
ELSE 'Aktiv'
END as status,
(SELECT COUNT(*) FROM sessions s WHERE s.license_key = l.license_key AND s.is_active = true) as active_sessions,
(SELECT COUNT(DISTINCT hardware_id) FROM sessions s WHERE s.license_key = l.license_key) as registered_devices
FROM licenses l
LEFT JOIN customers c ON l.customer_id = c.id
WHERE l.is_fake = false
ORDER BY l.created_at DESC
"""
cur.execute(query)
@@ -85,19 +56,25 @@ def export_licenses():
'Status', 'Aktive Sessions', 'Registrierte Geräte']
for row in cur.fetchall():
data.append(list(row))
row_data = list(row)
# Format datetime fields
if row_data[5]: # valid_from
row_data[5] = format_datetime_for_export(row_data[5])
if row_data[6]: # valid_until
row_data[6] = format_datetime_for_export(row_data[6])
if row_data[9]: # created_at
row_data[9] = format_datetime_for_export(row_data[9])
data.append(row_data)
# Excel-Datei erstellen
excel_file = create_excel_export(data, columns, 'Lizenzen')
# Format prüfen
format_type = request.args.get('format', 'excel').lower()
# Datei senden
filename = f"lizenzen_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
return send_file(
excel_file,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name=filename
)
if format_type == 'csv':
# CSV-Datei erstellen
return create_csv_export(data, columns, 'lizenzen')
else:
# Excel-Datei erstellen
return create_excel_export(data, columns, 'lizenzen')
except Exception as e:
logging.error(f"Fehler beim Export: {str(e)}")
@@ -120,23 +97,61 @@ def export_audit():
action_filter = request.args.get('action', '')
entity_type_filter = request.args.get('entity_type', '')
# Query aufbauen
query = """
SELECT
id, timestamp, username, action, entity_type, entity_id,
ip_address, user_agent, old_values, new_values, additional_info
FROM audit_log
WHERE timestamp >= CURRENT_TIMESTAMP - INTERVAL '%s days'
"""
params = [days]
if action_filter:
query += " AND action = %s"
params.append(action_filter)
if entity_type_filter:
query += " AND entity_type = %s"
params.append(entity_type_filter)
query += " ORDER BY timestamp DESC"
cur.execute(query, params)
# Daten in Dictionary-Format umwandeln
audit_logs = []
for row in cur.fetchall():
audit_logs.append({
'id': row[0],
'timestamp': row[1],
'username': row[2],
'action': row[3],
'entity_type': row[4],
'entity_id': row[5],
'ip_address': row[6],
'user_agent': row[7],
'old_values': row[8],
'new_values': row[9],
'additional_info': row[10]
})
# Daten für Export vorbereiten
data = prepare_audit_export_data(days, action_filter, entity_type_filter)
data = prepare_audit_export_data(audit_logs)
# Excel-Datei erstellen
columns = ['Zeitstempel', 'Benutzer', 'Aktion', 'Entität', 'Entität ID',
'IP-Adresse', 'Alte Werte', 'Neue Werte', 'Zusatzinfo']
columns = ['ID', 'Zeitstempel', 'Benutzer', 'Aktion', 'Entität', 'Entität ID',
'IP-Adresse', 'User Agent', 'Alte Werte', 'Neue Werte', 'Zusatzinfo']
excel_file = create_excel_export(data, columns, 'Audit-Log')
# Format prüfen
format_type = request.args.get('format', 'excel').lower()
# Datei senden
filename = f"audit_log_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
return send_file(
excel_file,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name=filename
)
if format_type == 'csv':
# CSV-Datei erstellen
return create_csv_export(data, columns, 'audit_log')
else:
# Excel-Datei erstellen
return create_excel_export(data, columns, 'audit_log')
except Exception as e:
logging.error(f"Fehler beim Export: {str(e)}")
@@ -154,7 +169,7 @@ def export_customers():
cur = conn.cursor()
try:
# SQL Query
# SQL Query - nur reale Kunden exportieren
cur.execute("""
SELECT
c.id,
@@ -169,6 +184,7 @@ def export_customers():
COUNT(CASE WHEN l.valid_until < CURRENT_DATE THEN 1 END) as expired_licenses
FROM customers c
LEFT JOIN licenses l ON c.id = l.customer_id
WHERE c.is_fake = false
GROUP BY c.id, c.name, c.email, c.phone, c.address, c.created_at, c.is_fake
ORDER BY c.name
""")
@@ -179,19 +195,21 @@ def export_customers():
'Test-Kunde', 'Anzahl Lizenzen', 'Aktive Lizenzen', 'Abgelaufene Lizenzen']
for row in cur.fetchall():
data.append(list(row))
# Format datetime fields (created_at ist Spalte 5)
row_data = list(row)
if row_data[5]: # created_at
row_data[5] = format_datetime_for_export(row_data[5])
data.append(row_data)
# Excel-Datei erstellen
excel_file = create_excel_export(data, columns, 'Kunden')
# Format prüfen
format_type = request.args.get('format', 'excel').lower()
# Datei senden
filename = f"kunden_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
return send_file(
excel_file,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name=filename
)
if format_type == 'csv':
# CSV-Datei erstellen
return create_csv_export(data, columns, 'kunden')
else:
# Excel-Datei erstellen
return create_excel_export(data, columns, 'kunden')
except Exception as e:
logging.error(f"Fehler beim Export: {str(e)}")
@@ -230,7 +248,7 @@ def export_sessions():
l.is_fake
FROM sessions s
LEFT JOIN licenses l ON s.license_key = l.license_key
WHERE s.is_active = true
WHERE s.is_active = true AND l.is_fake = false
ORDER BY s.started_at DESC
"""
cur.execute(query)
@@ -250,7 +268,7 @@ def export_sessions():
l.is_fake
FROM sessions s
LEFT JOIN licenses l ON s.license_key = l.license_key
WHERE s.started_at >= CURRENT_TIMESTAMP - INTERVAL '%s days'
WHERE s.started_at >= CURRENT_TIMESTAMP - INTERVAL '%s days' AND l.is_fake = false
ORDER BY s.started_at DESC
"""
cur.execute(query, (days,))
@@ -262,19 +280,25 @@ def export_sessions():
'Lizenztyp', 'Fake-Lizenz']
for row in cur.fetchall():
data.append(list(row))
row_data = list(row)
# Format datetime fields
if row_data[5]: # started_at
row_data[5] = format_datetime_for_export(row_data[5])
if row_data[6]: # ended_at
row_data[6] = format_datetime_for_export(row_data[6])
if row_data[7]: # last_heartbeat
row_data[7] = format_datetime_for_export(row_data[7])
data.append(row_data)
# Excel-Datei erstellen
excel_file = create_excel_export(data, columns, 'Sessions')
# Format prüfen
format_type = request.args.get('format', 'excel').lower()
# Datei senden
filename = f"sessions_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
return send_file(
excel_file,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name=filename
)
if format_type == 'csv':
# CSV-Datei erstellen
return create_csv_export(data, columns, 'sessions')
else:
# Excel-Datei erstellen
return create_excel_export(data, columns, 'sessions')
except Exception as e:
logging.error(f"Fehler beim Export: {str(e)}")
@@ -295,7 +319,6 @@ def export_resources():
# Filter aus Request
resource_type = request.args.get('type', 'all')
status_filter = request.args.get('status', 'all')
show_fake = request.args.get('show_fake', 'false') == 'true'
# SQL Query aufbauen
query = """
@@ -328,8 +351,8 @@ def export_resources():
query += " AND rp.status = %s"
params.append(status_filter)
if not show_fake:
query += " AND rp.is_fake = false"
# Immer nur reale Ressourcen exportieren
query += " AND rp.is_fake = false"
query += " ORDER BY rp.resource_type, rp.resource_value"
@@ -342,23 +365,131 @@ def export_resources():
'Status geändert von', 'Quarantäne-Grund']
for row in cur.fetchall():
data.append(list(row))
row_data = list(row)
# Format datetime fields
if row_data[7]: # created_at
row_data[7] = format_datetime_for_export(row_data[7])
if row_data[9]: # status_changed_at
row_data[9] = format_datetime_for_export(row_data[9])
data.append(row_data)
# Excel-Datei erstellen
excel_file = create_excel_export(data, columns, 'Ressourcen')
# Format prüfen
format_type = request.args.get('format', 'excel').lower()
# Datei senden
filename = f"ressourcen_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
return send_file(
excel_file,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name=filename
)
if format_type == 'csv':
# CSV-Datei erstellen
return create_csv_export(data, columns, 'ressourcen')
else:
# Excel-Datei erstellen
return create_excel_export(data, columns, 'ressourcen')
except Exception as e:
logging.error(f"Fehler beim Export: {str(e)}")
return "Fehler beim Exportieren der Ressourcen", 500
finally:
cur.close()
conn.close()
@export_bp.route("/monitoring")
@login_required
def export_monitoring():
"""Exportiert Monitoring-Daten als Excel/CSV-Datei"""
conn = get_connection()
cur = conn.cursor()
try:
# Zeitraum aus Request
hours = int(request.args.get('hours', 24))
# Monitoring-Daten sammeln
data = []
columns = ['Zeitstempel', 'Lizenz-ID', 'Lizenzschlüssel', 'Kunde', 'Hardware-ID',
'IP-Adresse', 'Ereignis-Typ', 'Schweregrad', 'Beschreibung']
# Query für Heartbeats und optionale Anomalien
query = """
WITH monitoring_data AS (
-- Lizenz-Heartbeats
SELECT
lh.timestamp,
lh.license_id,
l.license_key,
c.name as customer_name,
lh.hardware_id,
lh.ip_address,
'Heartbeat' as event_type,
'Normal' as severity,
'License validation' as description
FROM license_heartbeats lh
JOIN licenses l ON l.id = lh.license_id
JOIN customers c ON c.id = l.customer_id
WHERE lh.timestamp > CURRENT_TIMESTAMP - INTERVAL '%s hours'
AND l.is_fake = false
"""
# Check if anomaly_detections table exists
cur.execute("""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = 'anomaly_detections'
)
""")
has_anomalies = cur.fetchone()[0]
if has_anomalies:
query += """
UNION ALL
-- Anomalien
SELECT
ad.detected_at as timestamp,
ad.license_id,
l.license_key,
c.name as customer_name,
ad.hardware_id,
ad.ip_address,
ad.anomaly_type as event_type,
ad.severity,
ad.description
FROM anomaly_detections ad
LEFT JOIN licenses l ON l.id = ad.license_id
LEFT JOIN customers c ON c.id = l.customer_id
WHERE ad.detected_at > CURRENT_TIMESTAMP - INTERVAL '%s hours'
AND (l.is_fake = false OR l.is_fake IS NULL)
"""
params = [hours, hours]
else:
params = [hours]
query += """
)
SELECT * FROM monitoring_data
ORDER BY timestamp DESC
"""
cur.execute(query, params)
for row in cur.fetchall():
row_data = list(row)
# Format datetime field (timestamp ist Spalte 0)
if row_data[0]: # timestamp
row_data[0] = format_datetime_for_export(row_data[0])
data.append(row_data)
# Format prüfen
format_type = request.args.get('format', 'excel').lower()
if format_type == 'csv':
# CSV-Datei erstellen
return create_csv_export(data, columns, 'monitoring')
else:
# Excel-Datei erstellen
return create_excel_export(data, columns, 'monitoring')
except Exception as e:
logging.error(f"Fehler beim Export: {str(e)}")
return "Fehler beim Exportieren der Monitoring-Daten", 500
finally:
cur.close()
conn.close()

Datei anzeigen

@@ -193,16 +193,14 @@
<div class="col-md-3">
<div class="d-flex gap-2">
<a href="{{ url_for('admin.audit_log') }}" class="btn btn-outline-secondary">Zurücksetzen</a>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-download"></i> Export
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('export.export_audit', format='excel', user=filter_user, action=filter_action, entity=filter_entity) }}">
<i class="bi bi-file-earmark-excel text-success"></i> Excel Export</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_audit', format='csv', user=filter_user, action=filter_action, entity=filter_entity) }}">
<i class="bi bi-file-earmark-text"></i> CSV Export</a></li>
</ul>
<!-- Export Buttons -->
<div class="btn-group" role="group">
<a href="{{ url_for('export.export_audit', format='excel', user=filter_user, action=filter_action, entity=filter_entity) }}" class="btn btn-success btn-sm">
<i class="bi bi-file-earmark-excel"></i> Excel
</a>
<a href="{{ url_for('export.export_audit', format='csv', user=filter_user, action=filter_action, entity=filter_entity) }}" class="btn btn-secondary btn-sm">
<i class="bi bi-file-earmark-text"></i> CSV
</a>
</div>
</div>
</div>

Datei anzeigen

@@ -12,21 +12,20 @@
<a href="{{ url_for('leads.institutions') }}" class="btn btn-primary">
<i class="bi bi-people"></i> Leads
</a>
<div class="dropdown d-inline-block">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-download"></i> Export
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('export.export_customers', format='excel', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-excel text-success"></i> Kunden (Excel)</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_customers', format='csv', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-text"></i> Kunden (CSV)</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_licenses', format='excel', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-excel text-success"></i> Lizenzen (Excel)</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_licenses', format='csv', include_test=request.args.get('show_fake')) }}">
<i class="bi bi-file-earmark-text"></i> Lizenzen (CSV)</a></li>
</ul>
<!-- Export Buttons ohne Dropdown -->
<div class="btn-group" role="group">
<a href="{{ url_for('export.export_customers', format='excel') }}" class="btn btn-success btn-sm">
<i class="bi bi-file-earmark-excel"></i> Kunden Excel
</a>
<a href="{{ url_for('export.export_customers', format='csv') }}" class="btn btn-secondary btn-sm">
<i class="bi bi-file-earmark-text"></i> Kunden CSV
</a>
<a href="{{ url_for('export.export_licenses', format='excel') }}" class="btn btn-success btn-sm">
<i class="bi bi-file-earmark-excel"></i> Lizenzen Excel
</a>
<a href="{{ url_for('export.export_licenses', format='csv') }}" class="btn btn-secondary btn-sm">
<i class="bi bi-file-earmark-text"></i> Lizenzen CSV
</a>
</div>
</div>
</div>

Datei anzeigen

@@ -301,9 +301,6 @@
<div class="analytics-card">
<h5>Berichte exportieren</h5>
<div class="export-buttons">
<button class="btn btn-outline-primary me-2" onclick="exportReport('pdf')">
<i class="bi bi-file-pdf"></i> PDF Export
</button>
<button class="btn btn-outline-success me-2" onclick="exportReport('excel')">
<i class="bi bi-file-excel"></i> Excel Export
</button>
@@ -435,7 +432,9 @@
}
function exportReport(format) {
alert(`Export-Funktion wird implementiert für Format: ${format.toUpperCase()}`);
// Redirect to export endpoint with format parameter
const hours = 24; // Default to 24 hours
window.location.href = `/export/monitoring?format=${format}&hours=${hours}`;
}
// Start auto-refresh

Datei anzeigen

@@ -660,7 +660,9 @@
}
function exportReport(format) {
alert(`Export-Funktion wird implementiert für Format: ${format.toUpperCase()}`);
// Redirect to export endpoint with format parameter
const hours = 24; // Default to 24 hours
window.location.href = `/export/monitoring?format=${format}&hours=${hours}`;
}
// Countdown timer

Datei anzeigen

@@ -56,23 +56,23 @@
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Session-Tracking</h2>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="bi bi-download"></i> Export
</button>
<ul class="dropdown-menu">
<li><h6 class="dropdown-header">Aktive Sessions</h6></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_sessions', type='active', format='excel') }}">
<i class="bi bi-file-earmark-excel text-success"></i> Excel Export</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_sessions', type='active', format='csv') }}">
<i class="bi bi-file-earmark-text"></i> CSV Export</a></li>
<li><hr class="dropdown-divider"></li>
<li><h6 class="dropdown-header">Beendete Sessions</h6></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_sessions', type='ended', format='excel') }}">
<i class="bi bi-file-earmark-excel text-success"></i> Excel Export</a></li>
<li><a class="dropdown-item" href="{{ url_for('export.export_sessions', type='ended', format='csv') }}">
<i class="bi bi-file-earmark-text"></i> CSV Export</a></li>
</ul>
<!-- Export Buttons -->
<div>
<span class="text-muted me-2">Export:</span>
<div class="btn-group" role="group">
<a href="{{ url_for('export.export_sessions', active_only='true', format='excel') }}" class="btn btn-success btn-sm">
<i class="bi bi-file-earmark-excel"></i> Aktive (Excel)
</a>
<a href="{{ url_for('export.export_sessions', active_only='true', format='csv') }}" class="btn btn-secondary btn-sm">
<i class="bi bi-file-earmark-text"></i> Aktive (CSV)
</a>
<a href="{{ url_for('export.export_sessions', format='excel') }}" class="btn btn-success btn-sm">
<i class="bi bi-file-earmark-excel"></i> Alle (Excel)
</a>
<a href="{{ url_for('export.export_sessions', format='csv') }}" class="btn btn-secondary btn-sm">
<i class="bi bi-file-earmark-text"></i> Alle (CSV)
</a>
</div>
</div>
</div>

Datei anzeigen

@@ -1,9 +1,10 @@
import pandas as pd
from io import BytesIO
from io import BytesIO, StringIO
from datetime import datetime
from zoneinfo import ZoneInfo
from openpyxl.utils import get_column_letter
from flask import send_file
import csv
def create_excel_export(data, columns, filename_prefix="export"):
@@ -35,6 +36,34 @@ def create_excel_export(data, columns, filename_prefix="export"):
)
def create_csv_export(data, columns, filename_prefix="export"):
"""Create a CSV file from data"""
# Create CSV in memory
output = StringIO()
writer = csv.writer(output)
# Write header
writer.writerow(columns)
# Write data
writer.writerows(data)
# Convert to bytes
output.seek(0)
output_bytes = BytesIO(output.getvalue().encode('utf-8-sig')) # UTF-8 with BOM for Excel compatibility
# Generate filename with timestamp
timestamp = datetime.now(ZoneInfo("Europe/Berlin")).strftime('%Y%m%d_%H%M%S')
filename = f"{filename_prefix}_{timestamp}.csv"
return send_file(
output_bytes,
mimetype='text/csv',
as_attachment=True,
download_name=filename
)
def format_datetime_for_export(dt):
"""Format datetime for export"""
if dt:
@@ -43,6 +72,9 @@ def format_datetime_for_export(dt):
dt = datetime.fromisoformat(dt)
except:
return dt
# Remove timezone info for Excel compatibility
if hasattr(dt, 'replace') and dt.tzinfo is not None:
dt = dt.replace(tzinfo=None)
return dt.strftime('%Y-%m-%d %H:%M:%S')
return ''