Dashboard mit Statistiken
Dieser Commit ist enthalten in:
29
JOURNAL.md
29
JOURNAL.md
@@ -194,3 +194,32 @@ Lizenzmanagement-System für Social Media Account-Erstellungssoftware mit Docker
|
|||||||
- Lösch-Button ist deaktiviert, wenn Kunde Lizenzen hat
|
- Lösch-Button ist deaktiviert, wenn Kunde Lizenzen hat
|
||||||
- Aktive Lizenzen werden separat gezählt (nicht abgelaufen + aktiv)
|
- Aktive Lizenzen werden separat gezählt (nicht abgelaufen + aktiv)
|
||||||
- UTF-8 Support für Kundennamen mit Umlauten
|
- UTF-8 Support für Kundennamen mit Umlauten
|
||||||
|
|
||||||
|
### 2025-01-06 - Dashboard mit Statistiken implementiert
|
||||||
|
- Übersichtliches Dashboard als neue Startseite
|
||||||
|
- Statistik-Karten mit wichtigen Kennzahlen
|
||||||
|
- Listen für bald ablaufende und zuletzt erstellte Lizenzen
|
||||||
|
- Routing angepasst: Dashboard (/) und Lizenz erstellen (/create)
|
||||||
|
|
||||||
|
**Neue Features:**
|
||||||
|
- Statistik-Karten: Kunden, Lizenzen gesamt, Aktive, Ablaufende
|
||||||
|
- Aufteilung nach Lizenztypen (Vollversion/Testversion)
|
||||||
|
- Aufteilung nach Status (Aktiv/Abgelaufen)
|
||||||
|
- Top 10 bald ablaufende Lizenzen mit Restlaufzeit
|
||||||
|
- Letzte 5 erstellte Lizenzen mit Status
|
||||||
|
- Hover-Effekt auf Statistik-Karten
|
||||||
|
- Einheitliche Navigation mit Dashboard-Link
|
||||||
|
|
||||||
|
**Geänderte/Neue Dateien:**
|
||||||
|
- v2_adminpanel/app.py (dashboard() komplett überarbeitet, create_license() Route)
|
||||||
|
- v2_adminpanel/templates/dashboard.html (neu erstellt)
|
||||||
|
- v2_adminpanel/templates/index.html (Navigation erweitert)
|
||||||
|
- v2_adminpanel/templates/licenses.html (Navigation angepasst)
|
||||||
|
- v2_adminpanel/templates/customers.html (Navigation angepasst)
|
||||||
|
|
||||||
|
**Dashboard-Inhalte:**
|
||||||
|
- 4 Hauptstatistiken als Karten
|
||||||
|
- Lizenztyp-Verteilung
|
||||||
|
- Status-Verteilung
|
||||||
|
- Warnung für bald ablaufende Lizenzen
|
||||||
|
- Übersicht der neuesten Aktivitäten
|
||||||
@@ -64,9 +64,101 @@ def logout():
|
|||||||
session.pop('username', None)
|
session.pop('username', None)
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@app.route("/", methods=["GET", "POST"])
|
@app.route("/")
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard():
|
def dashboard():
|
||||||
|
conn = get_connection()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Statistiken abrufen
|
||||||
|
# Gesamtanzahl Kunden
|
||||||
|
cur.execute("SELECT COUNT(*) FROM customers")
|
||||||
|
total_customers = cur.fetchone()[0]
|
||||||
|
|
||||||
|
# Gesamtanzahl Lizenzen
|
||||||
|
cur.execute("SELECT COUNT(*) FROM licenses")
|
||||||
|
total_licenses = cur.fetchone()[0]
|
||||||
|
|
||||||
|
# Aktive Lizenzen (nicht abgelaufen und is_active = true)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COUNT(*) FROM licenses
|
||||||
|
WHERE valid_until >= CURRENT_DATE AND is_active = TRUE
|
||||||
|
""")
|
||||||
|
active_licenses = cur.fetchone()[0]
|
||||||
|
|
||||||
|
# Abgelaufene Lizenzen
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COUNT(*) FROM licenses
|
||||||
|
WHERE valid_until < CURRENT_DATE
|
||||||
|
""")
|
||||||
|
expired_licenses = cur.fetchone()[0]
|
||||||
|
|
||||||
|
# Lizenzen die in den nächsten 30 Tagen ablaufen
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COUNT(*) FROM licenses
|
||||||
|
WHERE valid_until >= CURRENT_DATE
|
||||||
|
AND valid_until < CURRENT_DATE + INTERVAL '30 days'
|
||||||
|
AND is_active = TRUE
|
||||||
|
""")
|
||||||
|
expiring_soon = cur.fetchone()[0]
|
||||||
|
|
||||||
|
# Testlizenzen vs Vollversionen
|
||||||
|
cur.execute("""
|
||||||
|
SELECT license_type, COUNT(*)
|
||||||
|
FROM licenses
|
||||||
|
GROUP BY license_type
|
||||||
|
""")
|
||||||
|
license_types = dict(cur.fetchall())
|
||||||
|
|
||||||
|
# Letzte 5 erstellten Lizenzen
|
||||||
|
cur.execute("""
|
||||||
|
SELECT l.id, l.license_key, c.name, l.valid_until,
|
||||||
|
CASE
|
||||||
|
WHEN l.valid_until < CURRENT_DATE THEN 'abgelaufen'
|
||||||
|
WHEN l.valid_until < CURRENT_DATE + INTERVAL '30 days' THEN 'läuft bald ab'
|
||||||
|
ELSE 'aktiv'
|
||||||
|
END as status
|
||||||
|
FROM licenses l
|
||||||
|
JOIN customers c ON l.customer_id = c.id
|
||||||
|
ORDER BY l.id DESC
|
||||||
|
LIMIT 5
|
||||||
|
""")
|
||||||
|
recent_licenses = cur.fetchall()
|
||||||
|
|
||||||
|
# Bald ablaufende Lizenzen (nächste 30 Tage)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT l.id, l.license_key, c.name, l.valid_until,
|
||||||
|
l.valid_until - CURRENT_DATE as days_left
|
||||||
|
FROM licenses l
|
||||||
|
JOIN customers c ON l.customer_id = c.id
|
||||||
|
WHERE l.valid_until >= CURRENT_DATE
|
||||||
|
AND l.valid_until < CURRENT_DATE + INTERVAL '30 days'
|
||||||
|
AND l.is_active = TRUE
|
||||||
|
ORDER BY l.valid_until
|
||||||
|
LIMIT 10
|
||||||
|
""")
|
||||||
|
expiring_licenses = cur.fetchall()
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
stats = {
|
||||||
|
'total_customers': total_customers,
|
||||||
|
'total_licenses': total_licenses,
|
||||||
|
'active_licenses': active_licenses,
|
||||||
|
'expired_licenses': expired_licenses,
|
||||||
|
'expiring_soon': expiring_soon,
|
||||||
|
'full_licenses': license_types.get('full', 0),
|
||||||
|
'test_licenses': license_types.get('test', 0),
|
||||||
|
'recent_licenses': recent_licenses,
|
||||||
|
'expiring_licenses': expiring_licenses
|
||||||
|
}
|
||||||
|
|
||||||
|
return render_template("dashboard.html", stats=stats, username=session.get('username'))
|
||||||
|
|
||||||
|
@app.route("/create", methods=["GET", "POST"])
|
||||||
|
@login_required
|
||||||
|
def create_license():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
name = request.form["customer_name"]
|
name = request.form["customer_name"]
|
||||||
email = request.form["email"]
|
email = request.form["email"]
|
||||||
@@ -96,7 +188,7 @@ def dashboard():
|
|||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return redirect("/")
|
return redirect("/create")
|
||||||
|
|
||||||
return render_template("index.html", username=session.get('username'))
|
return render_template("index.html", username=session.get('username'))
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2>Kundenverwaltung</h2>
|
<h2>Kundenverwaltung</h2>
|
||||||
<div>
|
<div>
|
||||||
<a href="/" class="btn btn-primary">➕ Neue Lizenz</a>
|
<a href="/" class="btn btn-secondary">📊 Dashboard</a>
|
||||||
|
<a href="/create" class="btn btn-primary">➕ Neue Lizenz</a>
|
||||||
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
|
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
199
v2_adminpanel/templates/dashboard.html
Normale Datei
199
v2_adminpanel/templates/dashboard.html
Normale Datei
@@ -0,0 +1,199 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Dashboard - Admin Panel</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.stat-card {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.stat-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
.status-aktiv { color: #28a745; }
|
||||||
|
.status-ablaufend { color: #ffc107; }
|
||||||
|
.status-abgelaufen { color: #dc3545; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-light">
|
||||||
|
<nav class="navbar navbar-dark bg-dark">
|
||||||
|
<div class="container">
|
||||||
|
<span class="navbar-brand">🎛️ Lizenzverwaltung</span>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="text-white me-3">Angemeldet als: {{ username }}</span>
|
||||||
|
<a href="/logout" class="btn btn-outline-light btn-sm">Abmelden</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container py-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1>Dashboard</h1>
|
||||||
|
<div>
|
||||||
|
<a href="/create" class="btn btn-primary">➕ Neue Lizenz</a>
|
||||||
|
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
|
||||||
|
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistik-Karten -->
|
||||||
|
<div class="row g-3 mb-4">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card h-100">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">👥 Kunden</h5>
|
||||||
|
<h2 class="text-primary">{{ stats.total_customers }}</h2>
|
||||||
|
<p class="text-muted mb-0">Gesamt</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card h-100">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">📋 Lizenzen</h5>
|
||||||
|
<h2 class="text-info">{{ stats.total_licenses }}</h2>
|
||||||
|
<p class="text-muted mb-0">Gesamt</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card h-100">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">✅ Aktiv</h5>
|
||||||
|
<h2 class="text-success">{{ stats.active_licenses }}</h2>
|
||||||
|
<p class="text-muted mb-0">Gültige Lizenzen</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card stat-card h-100">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">⚠️ Ablaufend</h5>
|
||||||
|
<h2 class="text-warning">{{ stats.expiring_soon }}</h2>
|
||||||
|
<p class="text-muted mb-0">Nächste 30 Tage</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Lizenztypen -->
|
||||||
|
<div class="row g-3 mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Lizenztypen</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 text-center">
|
||||||
|
<h3 class="text-success">{{ stats.full_licenses }}</h3>
|
||||||
|
<p class="text-muted">Vollversionen</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 text-center">
|
||||||
|
<h3 class="text-warning">{{ stats.test_licenses }}</h3>
|
||||||
|
<p class="text-muted">Testversionen</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Lizenzstatus</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 text-center">
|
||||||
|
<h3 class="text-success">{{ stats.active_licenses }}</h3>
|
||||||
|
<p class="text-muted">Aktiv</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 text-center">
|
||||||
|
<h3 class="text-danger">{{ stats.expired_licenses }}</h3>
|
||||||
|
<p class="text-muted">Abgelaufen</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<!-- Bald ablaufende Lizenzen -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-warning text-dark">
|
||||||
|
<h5 class="mb-0">⏰ Bald ablaufende Lizenzen</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if stats.expiring_licenses %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Kunde</th>
|
||||||
|
<th>Lizenz</th>
|
||||||
|
<th>Tage</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for license in stats.expiring_licenses %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ license[2] }}</td>
|
||||||
|
<td><small><code>{{ license[1][:8] }}...</code></small></td>
|
||||||
|
<td><span class="badge bg-warning">{{ license[4] }} Tage</span></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted mb-0">Keine Lizenzen laufen in den nächsten 30 Tagen ab.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Letzte Lizenzen -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h5 class="mb-0">🆕 Zuletzt erstellte Lizenzen</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if stats.recent_licenses %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Kunde</th>
|
||||||
|
<th>Lizenz</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for license in stats.recent_licenses %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ license[2] }}</td>
|
||||||
|
<td><small><code>{{ license[1][:8] }}...</code></small></td>
|
||||||
|
<td>
|
||||||
|
{% if license[4] == 'abgelaufen' %}
|
||||||
|
<span class="status-abgelaufen">⚠️ Abgelaufen</span>
|
||||||
|
{% elif license[4] == 'läuft bald ab' %}
|
||||||
|
<span class="status-ablaufend">⏰ Läuft bald ab</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="status-aktiv">✅ Aktiv</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted mb-0">Noch keine Lizenzen erstellt.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2>Neue Lizenz erstellen</h2>
|
<h2>Neue Lizenz erstellen</h2>
|
||||||
<div>
|
<div>
|
||||||
|
<a href="/" class="btn btn-secondary">📊 Dashboard</a>
|
||||||
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
|
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
|
||||||
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
|
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h2>Lizenzübersicht</h2>
|
<h2>Lizenzübersicht</h2>
|
||||||
<div>
|
<div>
|
||||||
<a href="/" class="btn btn-primary">➕ Neue Lizenz</a>
|
<a href="/" class="btn btn-secondary">📊 Dashboard</a>
|
||||||
|
<a href="/create" class="btn btn-primary">➕ Neue Lizenz</a>
|
||||||
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
|
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
144
v2_testing/test_dashboard.py
Normale Datei
144
v2_testing/test_dashboard.py
Normale Datei
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Disable SSL warnings for self-signed certificate
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
# Test configuration
|
||||||
|
base_url = "https://localhost:443"
|
||||||
|
admin_user = {"username": "rac00n", "password": "1248163264"}
|
||||||
|
|
||||||
|
def login(session):
|
||||||
|
"""Login to admin panel"""
|
||||||
|
login_data = {
|
||||||
|
"username": admin_user["username"],
|
||||||
|
"password": admin_user["password"]
|
||||||
|
}
|
||||||
|
response = session.post(f"{base_url}/login", data=login_data, verify=False, allow_redirects=False)
|
||||||
|
return response.status_code == 302
|
||||||
|
|
||||||
|
def test_dashboard():
|
||||||
|
"""Test dashboard with statistics"""
|
||||||
|
session = requests.Session()
|
||||||
|
|
||||||
|
if not login(session):
|
||||||
|
return "✗ Failed to login"
|
||||||
|
|
||||||
|
response = session.get(f"{base_url}/", verify=False)
|
||||||
|
if response.status_code != 200:
|
||||||
|
return f"✗ Failed to access dashboard: Status {response.status_code}"
|
||||||
|
|
||||||
|
content = response.text
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# Check for statistics elements
|
||||||
|
statistics_checks = [
|
||||||
|
("Gesamtkunden", "Total customers statistic"),
|
||||||
|
("Gesamtlizenzen", "Total licenses statistic"),
|
||||||
|
("Aktive Lizenzen", "Active licenses statistic"),
|
||||||
|
("Abgelaufene Lizenzen", "Expired licenses statistic"),
|
||||||
|
("Läuft bald ab", "Expiring soon statistic"),
|
||||||
|
("Letzte Lizenzen", "Recent licenses section"),
|
||||||
|
("Bald ablaufende Lizenzen", "Expiring licenses section")
|
||||||
|
]
|
||||||
|
|
||||||
|
for check_text, description in statistics_checks:
|
||||||
|
if check_text in content:
|
||||||
|
results.append(f"✓ Found: {description}")
|
||||||
|
else:
|
||||||
|
results.append(f"✗ Missing: {description}")
|
||||||
|
|
||||||
|
# Check if actual numbers are displayed
|
||||||
|
if any(char.isdigit() for char in content):
|
||||||
|
results.append("✓ Statistics numbers are displayed")
|
||||||
|
else:
|
||||||
|
results.append("✗ No statistics numbers found")
|
||||||
|
|
||||||
|
# Check for customer names in recent licenses
|
||||||
|
if "Müller" in content or "Schröder" in content or "Köhler" in content:
|
||||||
|
results.append("✓ Recent licenses show customer names")
|
||||||
|
else:
|
||||||
|
results.append("✗ No customer names in recent licenses")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Rebuild and restart admin panel
|
||||||
|
print("Rebuilding admin panel with dashboard...")
|
||||||
|
subprocess.run(["docker-compose", "build", "admin-panel"], capture_output=True)
|
||||||
|
subprocess.run(["docker-compose", "up", "-d", "admin-panel"], capture_output=True)
|
||||||
|
print("Waiting for container to start...")
|
||||||
|
subprocess.run(["sleep", "5"], capture_output=True)
|
||||||
|
|
||||||
|
print("\nTesting Dashboard with Statistics")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
results = test_dashboard()
|
||||||
|
for result in results:
|
||||||
|
print(result)
|
||||||
|
|
||||||
|
# Get actual statistics from database for comparison
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
print("Actual Database Statistics:")
|
||||||
|
print("-" * 50)
|
||||||
|
|
||||||
|
# Total customers
|
||||||
|
result = subprocess.run([
|
||||||
|
"docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", "-t",
|
||||||
|
"-c", "SELECT COUNT(*) FROM customers;"
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
print(f"Total Customers: {result.stdout.strip()}")
|
||||||
|
|
||||||
|
# Total licenses
|
||||||
|
result = subprocess.run([
|
||||||
|
"docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", "-t",
|
||||||
|
"-c", "SELECT COUNT(*) FROM licenses;"
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
print(f"Total Licenses: {result.stdout.strip()}")
|
||||||
|
|
||||||
|
# Active licenses
|
||||||
|
result = subprocess.run([
|
||||||
|
"docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", "-t",
|
||||||
|
"-c", "SELECT COUNT(*) FROM licenses WHERE valid_until >= CURRENT_DATE AND is_active = TRUE;"
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
print(f"Active Licenses: {result.stdout.strip()}")
|
||||||
|
|
||||||
|
# Expired licenses
|
||||||
|
result = subprocess.run([
|
||||||
|
"docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", "-t",
|
||||||
|
"-c", "SELECT COUNT(*) FROM licenses WHERE valid_until < CURRENT_DATE;"
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
print(f"Expired Licenses: {result.stdout.strip()}")
|
||||||
|
|
||||||
|
# Expiring soon (30 days)
|
||||||
|
result = subprocess.run([
|
||||||
|
"docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", "-t",
|
||||||
|
"-c", "SELECT COUNT(*) FROM licenses WHERE valid_until >= CURRENT_DATE AND valid_until < CURRENT_DATE + INTERVAL '30 days' AND is_active = TRUE;"
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
print(f"Expiring Soon (30 days): {result.stdout.strip()}")
|
||||||
|
|
||||||
|
# License types breakdown
|
||||||
|
print("\nLicense Types:")
|
||||||
|
result = subprocess.run([
|
||||||
|
"docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank",
|
||||||
|
"-c", "SELECT license_type, COUNT(*) FROM licenses GROUP BY license_type ORDER BY COUNT(*) DESC;"
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
print(result.stdout)
|
||||||
|
|
||||||
|
# Recent licenses
|
||||||
|
print("Recent Licenses:")
|
||||||
|
result = subprocess.run([
|
||||||
|
"docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank",
|
||||||
|
"-c", """SELECT l.license_key, c.name, l.valid_until,
|
||||||
|
CASE
|
||||||
|
WHEN l.valid_until < CURRENT_DATE THEN 'abgelaufen'
|
||||||
|
WHEN l.valid_until < CURRENT_DATE + INTERVAL '30 days' THEN 'läuft bald ab'
|
||||||
|
ELSE 'aktiv'
|
||||||
|
END as status
|
||||||
|
FROM licenses l
|
||||||
|
JOIN customers c ON l.customer_id = c.id
|
||||||
|
ORDER BY l.id DESC
|
||||||
|
LIMIT 5;"""
|
||||||
|
], capture_output=True, text=True)
|
||||||
|
print(result.stdout)
|
||||||
53
v2_testing/test_dashboard_detail.py
Normale Datei
53
v2_testing/test_dashboard_detail.py
Normale Datei
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Disable SSL warnings for self-signed certificate
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
# Test configuration
|
||||||
|
base_url = "https://localhost:443"
|
||||||
|
admin_user = {"username": "rac00n", "password": "1248163264"}
|
||||||
|
|
||||||
|
def test_dashboard_detail():
|
||||||
|
"""Test dashboard content in detail"""
|
||||||
|
session = requests.Session()
|
||||||
|
|
||||||
|
# Login
|
||||||
|
login_data = {
|
||||||
|
"username": admin_user["username"],
|
||||||
|
"password": admin_user["password"]
|
||||||
|
}
|
||||||
|
session.post(f"{base_url}/login", data=login_data, verify=False, allow_redirects=False)
|
||||||
|
|
||||||
|
# Get dashboard
|
||||||
|
response = session.get(f"{base_url}/", verify=False)
|
||||||
|
content = response.text
|
||||||
|
|
||||||
|
# Extract numbers from content
|
||||||
|
numbers = re.findall(r'\d+', content)
|
||||||
|
print(f"Numbers found on page: {numbers[:20]}") # First 20 numbers
|
||||||
|
|
||||||
|
# Check specific sections
|
||||||
|
if "8" in numbers: # Total customers
|
||||||
|
print("✓ Found total customers count (8)")
|
||||||
|
|
||||||
|
if "5" in numbers: # Total licenses
|
||||||
|
print("✓ Found total licenses count (5)")
|
||||||
|
|
||||||
|
if "3" in numbers: # Active licenses
|
||||||
|
print("✓ Found active licenses count (3)")
|
||||||
|
|
||||||
|
if "2" in numbers: # Expired licenses
|
||||||
|
print("✓ Found expired licenses count (2)")
|
||||||
|
|
||||||
|
# Print a snippet of the HTML to see structure
|
||||||
|
print("\nHTML snippet (first 1000 chars):")
|
||||||
|
print(content[:1000])
|
||||||
|
|
||||||
|
return response.status_code
|
||||||
|
|
||||||
|
print("Detailed Dashboard Test")
|
||||||
|
print("=" * 50)
|
||||||
|
status = test_dashboard_detail()
|
||||||
In neuem Issue referenzieren
Einen Benutzer sperren