Fix: Werktagsberechnung robust, Checkbox-Auswertung verbessert, Tests angepasst

This commit is contained in:
2025-07-24 15:31:55 +02:00
parent b71bca3bb4
commit 0674849b6c
3 changed files with 44 additions and 51 deletions

20
app.py
View File

@@ -47,28 +47,22 @@ def index():
with open(log_path, 'a', encoding='utf-8') as f: with open(log_path, 'a', encoding='utf-8') as f:
from datetime import datetime as dt from datetime import datetime as dt
f.write(f"{dt.now().isoformat()} FUNC: {action}\n") f.write(f"{dt.now().isoformat()} FUNC: {action}\n")
if action == 'tage': if action == 'tage_werktage':
active_idx = 0 active_idx = 0
start = request.form.get('start1') start = request.form.get('start1')
end = request.form.get('end1') end = request.form.get('end1')
is_werktage = request.form.get('werktage') in ('on', 'true', '1', True)
try: try:
d1 = datetime.strptime(start, '%Y-%m-%d') d1 = datetime.strptime(start, '%Y-%m-%d')
d2 = datetime.strptime(end, '%Y-%m-%d') d2 = datetime.strptime(end, '%Y-%m-%d')
if is_werktage:
if d1 > d2:
d1, d2 = d2, d1
tage = np.busday_count(d1.date(), (d2 + timedelta(days=1)).date())
else:
tage = abs((d2 - d1).days) tage = abs((d2 - d1).days)
except Exception: except Exception:
tage = 'Ungültige Eingabe' tage = 'Ungültige Eingabe'
elif action == 'werktage':
active_idx = 1
start = request.form.get('start2')
end = request.form.get('end2')
try:
d1 = datetime.strptime(start, '%Y-%m-%d')
d2 = datetime.strptime(end, '%Y-%m-%d')
if d1 > d2:
d1, d2 = d2, d1
werktage = np.busday_count(d1.date(), (d2 + timedelta(days=1)).date())
except Exception:
werktage = 'Ungültige Eingabe'
elif action == 'wochentag': elif action == 'wochentag':
active_idx = 2 active_idx = 2
datum = request.form.get('datum3') datum = request.form.get('datum3')

View File

@@ -55,7 +55,7 @@
<!-- Kalender mit Doppelpfeil --> <!-- Kalender mit Doppelpfeil -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><path d="M8 15h8M8 15l2-2M8 15l2 2M16 15l-2-2M16 15l-2 2" stroke="#2563eb" stroke-width="1.5" stroke-linecap="round"/></svg> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><path d="M8 15h8M8 15l2-2M8 15l2 2M16 15l-2-2M16 15l-2 2" stroke="#2563eb" stroke-width="1.5" stroke-linecap="round"/></svg>
</span> </span>
Anzahl der Tage zwischen zwei Daten Anzahl der Tage/Werktage zwischen zwei Daten
</button> </button>
<div class="accordion-content"> <div class="accordion-content">
<form method="post"> <form method="post">
@@ -71,39 +71,20 @@
<button type="button" class="today-btn" onclick="setToday('end1')">Heute</button> <button type="button" class="today-btn" onclick="setToday('end1')">Heute</button>
</span> </span>
</label> </label>
<button name="action" value="tage" type="submit">Berechnen</button> <label style="display:flex;align-items:center;gap:0.5em;margin-top:0.7em;">
<input type="checkbox" name="werktage" id="werktage" {% if request.form.get('werktage') %}checked{% endif %}>
Nur Werktage
</label>
<button name="action" value="tage_werktage" type="submit">Berechnen</button>
</form> </form>
{% if tage is not none %} {% if tage is not none %}
<div class="result">Anzahl der Tage zwischen <b>{{ format_date(request.form.get('start1', '')) }}</b> und <b>{{ format_date(request.form.get('end1', '')) }}</b>: {{ tage }}</div> <div class="result">
{% if request.form.get('werktage') %}
Anzahl der Werktage zwischen <b>{{ format_date(request.form.get('start1', '')) }}</b> und <b>{{ format_date(request.form.get('end1', '')) }}</b>: {{ tage }}
{% else %}
Anzahl der Tage zwischen <b>{{ format_date(request.form.get('start1', '')) }}</b> und <b>{{ format_date(request.form.get('end1', '')) }}</b>: {{ tage }}
{% endif %} {% endif %}
</div> </div>
</div>
<div class="accordion-item">
<button type="button" class="accordion-header header-werktage" onclick="openAccordion(1)">
<span style="vertical-align:middle;display:inline-block;width:1.5em;">
<!-- Kalender mit Mo-Fr Symbol -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="5" width="18" height="16" rx="4" fill="#fff" stroke="#2563eb" stroke-width="2"/><rect x="3" y="5" width="18" height="4" rx="2" fill="#2563eb"/><rect x="6" y="2" width="2" height="4" rx="1" fill="#2563eb"/><rect x="16" y="2" width="2" height="4" rx="1" fill="#2563eb"/><text x="12" y="17" text-anchor="middle" font-size="8" font-family="Segoe UI, Arial, sans-serif" fill="#2563eb" font-weight="bold">Mo-Fr</text></svg>
</span>
Anzahl der Werktage zwischen zwei Daten
</button>
<div class="accordion-content">
<form method="post">
<label>Startdatum:<br>
<span class="date-row">
<input type="date" name="start2" id="start2">
<button type="button" class="today-btn" onclick="setToday('start2')">Heute</button>
</span>
</label>
<label>Enddatum:<br>
<span class="date-row">
<input type="date" name="end2" id="end2">
<button type="button" class="today-btn" onclick="setToday('end2')">Heute</button>
</span>
</label>
<button name="action" value="werktage" type="submit">Berechnen</button>
</form>
{% if werktage is not none %}
<div class="result">Anzahl der Werktage zwischen <b>{{ format_date(request.form.get('start2', '')) }}</b> und <b>{{ format_date(request.form.get('end2', '')) }}</b>: {{ werktage }}</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -15,7 +15,7 @@ def test_homepage(client):
def test_tage_berechnung(client): def test_tage_berechnung(client):
resp = client.post('/', data={ resp = client.post('/', data={
'action': 'tage', 'action': 'tage_werktage',
'start1': '2024-01-01', 'start1': '2024-01-01',
'end1': '2024-01-10' 'end1': '2024-01-10'
}) })
@@ -27,16 +27,34 @@ def test_xss_protection(client):
# Versuche ein Skript einzuschleusen # Versuche ein Skript einzuschleusen
xss = '<script>alert(1)</script>' xss = '<script>alert(1)</script>'
resp = client.post('/', data={ resp = client.post('/', data={
'action': 'tage', 'action': 'tage_werktage',
'start1': xss, 'start1': xss,
'end1': '2024-01-10' 'end1': '2024-01-10'
}) })
assert resp.status_code == 200 assert resp.status_code == 200
# Das Skript darf nicht im HTML erscheinen (sollte escaped sein) # Das Skript darf nicht im HTML erscheinen
assert b'<script>alert(1)</script>' not in resp.data assert b'<script>alert(1)</script>' not in resp.data
assert b'&lt;script&gt;alert(1)&lt;/script&gt;' in resp.data # Es sollte eine Fehlermeldung erscheinen
html = resp.data.decode('utf-8')
assert 'Ungültige Eingabe' in html
def test_stats_login_required(client): def test_stats_login_required(client):
resp = client.get('/stats') resp = client.get('/stats')
assert resp.status_code == 200 assert resp.status_code == 200
assert b'Dashboard Login' in resp.data assert b'Dashboard Login' in resp.data
def test_werktage_berechnung(client):
from numpy import busday_count
from datetime import date, timedelta
start = '2024-01-01'
end = '2024-03-01'
expected = busday_count(date.fromisoformat(start), date.fromisoformat(end) + timedelta(days=1))
resp = client.post('/', data={
'action': 'tage_werktage',
'start1': start,
'end1': end,
'werktage': 'on'
})
assert resp.status_code == 200
assert b'Anzahl der Werktage' in resp.data
assert f': {expected}'.encode() in resp.data