first Commit
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
venv/
|
||||
__pycache__/
|
||||
test*
|
||||
*.old
|
||||
.venv/
|
||||
.DS_Store
|
||||
.vscode
|
||||
|
||||
31
AGENTS.md
Normal file
31
AGENTS.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Stack
|
||||
- This is a single-file Flask app. The real application entrypoint is `app.py`; there is no package layout, test suite, or separate config module.
|
||||
- Runtime dependency is only `Flask==3.0.3` from `requirements.txt`.
|
||||
|
||||
## Run
|
||||
- Create the environment exactly as documented in `README.md`: `python3 -m venv .venv`, `source .venv/bin/activate`, `pip install -r requirements.txt`.
|
||||
- Start the app with `python3 app.py`. `app.py` calls `app.run(debug=True)` directly under `if __name__ == "__main__"`.
|
||||
- Default local URL is `http://127.0.0.1:5000`.
|
||||
|
||||
## Data And Side Effects
|
||||
- The app writes to repo-local files next to `app.py`: SQLite database `inventory.db` and log file `inventory.log`.
|
||||
- `init_db()` runs on every request via `@app.before_request`, so schema creation and the default admin bootstrap happen lazily through web traffic, not a separate init command.
|
||||
- The first admin user is auto-created with username `admin` and no password; first login redirects to `/set-password`.
|
||||
|
||||
## Architecture Notes
|
||||
- `app.py` owns routes, auth, schema management, logging, and business logic in one file. Read it before making cross-cutting changes.
|
||||
- Templates live in `templates/`; static assets are in `static/`.
|
||||
- Auth uses Flask session key `staff_user_id`. Access control is enforced with `login_required` and `admin_required` decorators in `app.py`.
|
||||
- The transaction history stores `handled_by`; `init_db()` also contains a lightweight migration that adds this column if missing. Preserve this pattern if making schema changes against existing `inventory.db` files.
|
||||
- Successful `/assign` and `/return` posts redirect to `/transactions/<id>/print`; the printable receipt flow is part of the normal workflow, not an optional extra page.
|
||||
- Admin-only behavior exists in both routes and templates: only admins can reach `/admin/staff`, and only admins see recent log entries on the dashboard.
|
||||
|
||||
## Verification
|
||||
- There are no configured tests, linters, type checks, CI workflows, or task runners in the repo.
|
||||
- For changes, the practical verification step is to run `python3 app.py` and exercise the relevant route flows manually in the browser.
|
||||
|
||||
## Editing Cautions
|
||||
- Treat `inventory.db`, `inventory.log`, and `__pycache__/` as runtime artifacts, not source files.
|
||||
- `SECRET_KEY` is hardcoded to `dev-secret-key` in `app.py`; do not assume environment-based config already exists.
|
||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 5006
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
66
README.md
Normal file
66
README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Verwaltung fuer Tuerchips und Parkkarten
|
||||
|
||||
## Voraussetzungen
|
||||
- Python 3.10 oder neuer
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Starten
|
||||
```bash
|
||||
python3 app.py
|
||||
```
|
||||
|
||||
Die Anwendung ist danach unter `http://127.0.0.1:5000` erreichbar.
|
||||
|
||||
## Funktionen
|
||||
- Anmeldung fuer Bearbeiter und Admins
|
||||
- Admin kann Bearbeiter und weitere Admins anlegen
|
||||
- Bearbeiter vergeben ihr Passwort bei der ersten Anmeldung selbst
|
||||
- Admin kann Passwort-Reset fuer Bearbeiter ausloesen
|
||||
- Admin kann vorhandene Bestandsdaten per CSV importieren
|
||||
- User anlegen
|
||||
- Ausgabe von Tuerchips und Parkkarten
|
||||
- Rueckgabe von Tuerchips und Parkkarten
|
||||
- Uebersicht mit Suche und letzten Bewegungen
|
||||
- Einfache Logdatei mit Datum, Medium und bearbeitendem Mitarbeiter
|
||||
- Anzeige der letzten Logeintraege im Webinterface
|
||||
- Bearbeiter wird auch in der Datenbankhistorie gespeichert
|
||||
- Doppelte Kennungen fuer Tuerchips und Parkkarten werden bei der Ausgabe verhindert
|
||||
|
||||
## Datenhaltung
|
||||
- Die Daten werden lokal in `inventory.db` als SQLite-Datenbank gespeichert.
|
||||
- Die einfache Dateiprotokollierung wird in `inventory.log` geschrieben.
|
||||
|
||||
## Anmeldung
|
||||
- Beim ersten Start wird automatisch ein Admin mit dem Benutzernamen `admin` angelegt.
|
||||
- Dieser Admin hat zunaechst kein Passwort und wird bei der ersten Anmeldung direkt zur Passwortvergabe gefuehrt.
|
||||
- Der Bearbeiter in der Historie und im Log ist immer der aktuell angemeldete Benutzer.
|
||||
|
||||
## Import vorhandener Daten
|
||||
- Nur Admins koennen den Import ueber den Menuepunkt `Import` aufrufen.
|
||||
- Der Import akzeptiert eine CSV-Datei mit Semikolon als Trennzeichen oder alternativ direkte Eingabe im Textfeld.
|
||||
- Die CSV-Datei muss UTF-8 kodiert sein.
|
||||
- Erwartetes Format pro Zeile:
|
||||
|
||||
```text
|
||||
User;Typ;Kennung;Aktion
|
||||
```
|
||||
|
||||
- Beispiel:
|
||||
|
||||
```text
|
||||
Max Mustermann;Tuerchip;CHIP-1001;Import
|
||||
Erika Muster;Parkkarte;PARK-2001;Import
|
||||
```
|
||||
|
||||
- Eine optionale Kopfzeile `User;Typ;Kennung;Aktion` wird automatisch erkannt und uebersprungen.
|
||||
|
||||
- Unterstuetzte Typen sind `Tuerchip` und `Parkkarte`.
|
||||
- Die Aktion `Import` uebernimmt vorhandene aktive Bestandsdaten in die Datenbank.
|
||||
- Falls ein User noch nicht existiert, wird er beim Import automatisch angelegt.
|
||||
- Bereits vergebene Kennungen oder widerspruechliche aktive Zuordnungen werden gesammelt als Fehler angezeigt; der Import wird erst ausgefuehrt, wenn keine Fehler mehr vorhanden sind.
|
||||
781
app.py
Normal file
781
app.py
Normal file
@@ -0,0 +1,781 @@
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import csv
|
||||
import io
|
||||
from functools import wraps
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Flask, flash, g, redirect, render_template, request, session, url_for
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config["SECRET_KEY"] = "dev-secret-key"
|
||||
APP_VERSION = "1.0.0"
|
||||
DATABASE = Path(__file__).with_name("inventory.db")
|
||||
LOGFILE = Path(__file__).with_name("inventory.log")
|
||||
|
||||
|
||||
logging.basicConfig(
|
||||
filename=LOGFILE,
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(levelname)s %(message)s",
|
||||
)
|
||||
|
||||
ASSET_LABELS = {
|
||||
"chip": "Tuerchip",
|
||||
"parking_card": "Parkkarte",
|
||||
}
|
||||
|
||||
ACTION_LABELS = {
|
||||
"assign": "Ausgabe",
|
||||
"return": "Rueckgabe",
|
||||
}
|
||||
|
||||
PRINT_DESCRIPTIONS = {
|
||||
"assign": "Empfangsbestaetigung fuer die Ausgabe eines Mediums",
|
||||
"return": "Rueckgabebestaetigung fuer die Ruecknahme eines Mediums",
|
||||
}
|
||||
|
||||
IMPORT_ACTIONS = {"import", "ausgabe", "assign"}
|
||||
IMPORT_HEADER = ["user", "typ", "kennung", "aktion"]
|
||||
|
||||
|
||||
def get_db() -> sqlite3.Connection:
|
||||
if "db" not in g:
|
||||
g.db = sqlite3.connect(DATABASE)
|
||||
g.db.row_factory = sqlite3.Row
|
||||
return g.db
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def inject_app_meta() -> dict[str, str | int]:
|
||||
host = request.host.split(":", 1)[0] if request.host else "-"
|
||||
return {
|
||||
"app_version": APP_VERSION,
|
||||
"app_host": host,
|
||||
"app_year": datetime.now().year,
|
||||
}
|
||||
|
||||
|
||||
@app.teardown_appcontext
|
||||
def close_db(_: object | None) -> None:
|
||||
db = g.pop("db", None)
|
||||
if db is not None:
|
||||
db.close()
|
||||
|
||||
|
||||
def init_db() -> None:
|
||||
db = get_db()
|
||||
db.executescript(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
full_name TEXT NOT NULL,
|
||||
email TEXT,
|
||||
department TEXT,
|
||||
chip_code TEXT,
|
||||
chip_assigned_at TEXT,
|
||||
parking_card_code TEXT,
|
||||
parking_card_assigned_at TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS transactions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
asset_type TEXT NOT NULL CHECK (asset_type IN ('chip', 'parking_card')),
|
||||
asset_code TEXT NOT NULL,
|
||||
handled_by TEXT,
|
||||
action TEXT NOT NULL CHECK (action IN ('assign', 'return')),
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS staff_users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
full_name TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK (role IN ('admin', 'staff')),
|
||||
password_hash TEXT,
|
||||
must_set_password INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
columns = {
|
||||
row["name"]
|
||||
for row in db.execute("PRAGMA table_info(transactions)").fetchall()
|
||||
}
|
||||
if "handled_by" not in columns:
|
||||
db.execute("ALTER TABLE transactions ADD COLUMN handled_by TEXT")
|
||||
|
||||
admin_exists = db.execute(
|
||||
"SELECT id FROM staff_users WHERE role = 'admin' LIMIT 1"
|
||||
).fetchone()
|
||||
if admin_exists is None:
|
||||
db.execute(
|
||||
"""
|
||||
INSERT INTO staff_users (username, full_name, role, password_hash, must_set_password)
|
||||
VALUES (?, ?, 'admin', NULL, 1)
|
||||
""",
|
||||
("admin", "Administrator"),
|
||||
)
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
def current_staff() -> sqlite3.Row | None:
|
||||
staff_id = session.get("staff_user_id")
|
||||
if not staff_id:
|
||||
return None
|
||||
return get_db().execute(
|
||||
"SELECT id, username, full_name, role, must_set_password FROM staff_users WHERE id = ?",
|
||||
(staff_id,),
|
||||
).fetchone()
|
||||
|
||||
|
||||
def login_required(view):
|
||||
@wraps(view)
|
||||
def wrapped_view(*args, **kwargs):
|
||||
staff = current_staff()
|
||||
endpoint = request.endpoint or ""
|
||||
allowed_without_password = {"set_password", "logout", "static"}
|
||||
|
||||
if staff is None:
|
||||
return redirect(url_for("login"))
|
||||
|
||||
if staff["must_set_password"] and endpoint not in allowed_without_password:
|
||||
flash("Bitte zuerst ein Passwort setzen.")
|
||||
return redirect(url_for("set_password"))
|
||||
|
||||
g.current_staff = staff
|
||||
return view(*args, **kwargs)
|
||||
|
||||
return wrapped_view
|
||||
|
||||
|
||||
def admin_required(view):
|
||||
@wraps(view)
|
||||
@login_required
|
||||
def wrapped_view(*args, **kwargs):
|
||||
if g.current_staff["role"] != "admin":
|
||||
flash("Nur Admins duerfen diese Seite aufrufen.")
|
||||
return redirect(url_for("index"))
|
||||
return view(*args, **kwargs)
|
||||
|
||||
return wrapped_view
|
||||
|
||||
|
||||
def log_asset_event(
|
||||
action: str,
|
||||
user_name: str,
|
||||
asset_type: str,
|
||||
asset_code: str,
|
||||
handled_by: str,
|
||||
) -> None:
|
||||
logging.info(
|
||||
"%s | user=%s | typ=%s | kennung=%s | bearbeiter=%s",
|
||||
action,
|
||||
user_name,
|
||||
ASSET_LABELS[asset_type],
|
||||
asset_code,
|
||||
handled_by,
|
||||
)
|
||||
|
||||
|
||||
def read_recent_logs(limit: int = 20) -> list[str]:
|
||||
if not LOGFILE.exists():
|
||||
return []
|
||||
lines = LOGFILE.read_text(encoding="utf-8").splitlines()
|
||||
return list(reversed(lines[-limit:]))
|
||||
|
||||
|
||||
def asset_code_in_use(db: sqlite3.Connection, asset_type: str, asset_code: str) -> bool:
|
||||
column_name = "chip_code" if asset_type == "chip" else "parking_card_code"
|
||||
existing = db.execute(
|
||||
f"SELECT id FROM users WHERE {column_name} = ?",
|
||||
(asset_code,),
|
||||
).fetchone()
|
||||
return existing is not None
|
||||
|
||||
|
||||
def normalize_asset_type(value: str) -> str | None:
|
||||
normalized = value.strip().lower()
|
||||
aliases = {
|
||||
"chip": "chip",
|
||||
"tuerchip": "chip",
|
||||
"parkkarte": "parking_card",
|
||||
"parking_card": "parking_card",
|
||||
"parking card": "parking_card",
|
||||
}
|
||||
return aliases.get(normalized)
|
||||
|
||||
|
||||
def is_import_header(parts: list[str]) -> bool:
|
||||
normalized = [part.strip().lower() for part in parts]
|
||||
return normalized == IMPORT_HEADER
|
||||
|
||||
|
||||
def get_transaction_for_print(transaction_id: int) -> sqlite3.Row | None:
|
||||
return get_db().execute(
|
||||
"""
|
||||
SELECT t.id, t.created_at, t.asset_type, t.asset_code, t.action, t.handled_by,
|
||||
u.full_name, u.email, u.department
|
||||
FROM transactions t
|
||||
JOIN users u ON u.id = t.user_id
|
||||
WHERE t.id = ?
|
||||
""",
|
||||
(transaction_id,),
|
||||
).fetchone()
|
||||
|
||||
|
||||
@app.before_request
|
||||
def ensure_database() -> None:
|
||||
init_db()
|
||||
g.current_staff = current_staff()
|
||||
|
||||
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login() -> str:
|
||||
if request.method == "POST":
|
||||
username = request.form.get("username", "").strip()
|
||||
password = request.form.get("password", "")
|
||||
db = get_db()
|
||||
staff = db.execute(
|
||||
"SELECT * FROM staff_users WHERE username = ?",
|
||||
(username,),
|
||||
).fetchone()
|
||||
|
||||
if staff is None:
|
||||
flash("Benutzer wurde nicht gefunden.")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
if staff["must_set_password"]:
|
||||
session.clear()
|
||||
session["staff_user_id"] = staff["id"]
|
||||
flash("Bitte jetzt Ihr Passwort vergeben.")
|
||||
return redirect(url_for("set_password"))
|
||||
|
||||
if not staff["password_hash"] or not check_password_hash(staff["password_hash"], password):
|
||||
flash("Login fehlgeschlagen.")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
session.clear()
|
||||
session["staff_user_id"] = staff["id"]
|
||||
flash("Anmeldung erfolgreich.")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
return render_template("login.html")
|
||||
|
||||
|
||||
@app.route("/set-password", methods=["GET", "POST"])
|
||||
def set_password() -> str:
|
||||
staff = current_staff()
|
||||
if staff is None:
|
||||
flash("Bitte zuerst anmelden.")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
if request.method == "POST":
|
||||
password = request.form.get("password", "")
|
||||
password_confirm = request.form.get("password_confirm", "")
|
||||
|
||||
if len(password) < 8:
|
||||
flash("Das Passwort muss mindestens 8 Zeichen lang sein.")
|
||||
return redirect(url_for("set_password"))
|
||||
|
||||
if password != password_confirm:
|
||||
flash("Die Passwoerter stimmen nicht ueberein.")
|
||||
return redirect(url_for("set_password"))
|
||||
|
||||
db = get_db()
|
||||
db.execute(
|
||||
"UPDATE staff_users SET password_hash = ?, must_set_password = 0 WHERE id = ?",
|
||||
(generate_password_hash(password), staff["id"]),
|
||||
)
|
||||
db.commit()
|
||||
flash("Passwort wurde gespeichert.")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
return render_template("set_password.html")
|
||||
|
||||
|
||||
@app.route("/logout")
|
||||
def logout() -> str:
|
||||
session.clear()
|
||||
flash("Sie wurden abgemeldet.")
|
||||
return redirect(url_for("login"))
|
||||
|
||||
|
||||
@app.route("/admin/staff", methods=["GET", "POST"])
|
||||
@admin_required
|
||||
def manage_staff() -> str:
|
||||
db = get_db()
|
||||
|
||||
if request.method == "POST":
|
||||
username = request.form.get("username", "").strip()
|
||||
full_name = request.form.get("full_name", "").strip()
|
||||
role = request.form.get("role", "staff").strip()
|
||||
|
||||
if not username or not full_name or role not in {"admin", "staff"}:
|
||||
flash("Bitte alle Bearbeiterdaten korrekt eingeben.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
try:
|
||||
db.execute(
|
||||
"INSERT INTO staff_users (username, full_name, role, password_hash, must_set_password) VALUES (?, ?, ?, NULL, 1)",
|
||||
(username, full_name, role),
|
||||
)
|
||||
db.commit()
|
||||
except sqlite3.IntegrityError:
|
||||
flash("Der Benutzername ist bereits vergeben.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
flash("Bearbeiter wurde angelegt. Passwort wird bei der ersten Anmeldung gesetzt.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
staff_users = db.execute(
|
||||
"SELECT id, username, full_name, role, must_set_password, created_at FROM staff_users ORDER BY full_name COLLATE NOCASE"
|
||||
).fetchall()
|
||||
return render_template("manage_staff.html", staff_users=staff_users)
|
||||
|
||||
|
||||
@app.route("/admin/staff/<int:staff_id>/reset-password", methods=["POST"])
|
||||
@admin_required
|
||||
def reset_staff_password(staff_id: int) -> str:
|
||||
db = get_db()
|
||||
staff = db.execute(
|
||||
"SELECT id FROM staff_users WHERE id = ?",
|
||||
(staff_id,),
|
||||
).fetchone()
|
||||
|
||||
if staff is None:
|
||||
flash("Bearbeiter wurde nicht gefunden.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
db.execute(
|
||||
"UPDATE staff_users SET password_hash = NULL, must_set_password = 1 WHERE id = ?",
|
||||
(staff_id,),
|
||||
)
|
||||
db.commit()
|
||||
flash("Passwort-Reset wurde gesetzt. Der Bearbeiter muss bei der naechsten Anmeldung ein neues Passwort vergeben.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
|
||||
@app.route("/admin/staff/<int:staff_id>/delete", methods=["POST"])
|
||||
@admin_required
|
||||
def delete_staff(staff_id: int) -> str:
|
||||
db = get_db()
|
||||
staff = db.execute(
|
||||
"SELECT id, role, full_name FROM staff_users WHERE id = ?",
|
||||
(staff_id,),
|
||||
).fetchone()
|
||||
|
||||
if staff is None:
|
||||
flash("Bearbeiter wurde nicht gefunden.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
if staff["id"] == g.current_staff["id"]:
|
||||
flash("Der aktuell angemeldete Bearbeiter kann nicht geloescht werden.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
if staff["role"] == "admin":
|
||||
admin_count = db.execute(
|
||||
"SELECT COUNT(*) AS admin_count FROM staff_users WHERE role = 'admin'"
|
||||
).fetchone()
|
||||
if admin_count["admin_count"] <= 1:
|
||||
flash("Der letzte Admin kann nicht geloescht werden.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
db.execute("DELETE FROM staff_users WHERE id = ?", (staff_id,))
|
||||
db.commit()
|
||||
flash(f"Bearbeiter '{staff['full_name']}' wurde geloescht.")
|
||||
return redirect(url_for("manage_staff"))
|
||||
|
||||
|
||||
@app.route("/admin/import", methods=["GET", "POST"])
|
||||
@admin_required
|
||||
def import_data() -> str:
|
||||
if request.method == "POST":
|
||||
upload = request.files.get("import_file")
|
||||
raw_rows = request.form.get("import_rows", "")
|
||||
|
||||
if upload and upload.filename:
|
||||
try:
|
||||
decoded = upload.stream.read().decode("utf-8-sig")
|
||||
except UnicodeDecodeError:
|
||||
flash("Die CSV-Datei muss UTF-8 kodiert sein.", "error")
|
||||
return redirect(url_for("import_data"))
|
||||
|
||||
csv_rows: list[str] = []
|
||||
reader = csv.reader(io.StringIO(decoded), delimiter=";")
|
||||
for row in reader:
|
||||
if not row or not any(cell.strip() for cell in row):
|
||||
continue
|
||||
csv_rows.append(";".join(cell.strip() for cell in row))
|
||||
raw_rows = "\n".join(csv_rows)
|
||||
|
||||
rows = [line.strip() for line in raw_rows.splitlines() if line.strip()]
|
||||
|
||||
if not rows:
|
||||
flash("Bitte mindestens eine Importzeile eingeben.", "error")
|
||||
return redirect(url_for("import_data"))
|
||||
|
||||
db = get_db()
|
||||
imported_count = 0
|
||||
errors: list[str] = []
|
||||
operations: list[tuple[str, object]] = []
|
||||
|
||||
for index, row in enumerate(rows, start=1):
|
||||
parts = [part.strip() for part in row.split(";")]
|
||||
if is_import_header(parts):
|
||||
continue
|
||||
if len(parts) != 4:
|
||||
errors.append(f"Zeile {index}: ungueltig. Erwartet wird: User;Typ;Kennung;Aktion")
|
||||
continue
|
||||
|
||||
full_name, raw_asset_type, asset_code, raw_action = parts
|
||||
asset_type = normalize_asset_type(raw_asset_type)
|
||||
action = raw_action.strip().lower()
|
||||
|
||||
if not full_name or not asset_type or not asset_code:
|
||||
errors.append(f"Zeile {index}: enthaelt unvollstaendige oder ungueltige Werte.")
|
||||
continue
|
||||
|
||||
if action not in IMPORT_ACTIONS:
|
||||
errors.append(f"Zeile {index}: ungueltige Aktion. Erlaubt: Import")
|
||||
continue
|
||||
|
||||
user = db.execute(
|
||||
"SELECT * FROM users WHERE full_name = ? COLLATE NOCASE",
|
||||
(full_name,),
|
||||
).fetchone()
|
||||
|
||||
pending_user = None
|
||||
for operation_type, payload in operations:
|
||||
if operation_type == "user" and payload["full_name"].lower() == full_name.lower():
|
||||
pending_user = payload
|
||||
break
|
||||
|
||||
current_chip_code = user["chip_code"] if user else None
|
||||
current_card_code = user["parking_card_code"] if user else None
|
||||
user_id = user["id"] if user else None
|
||||
|
||||
if pending_user is not None:
|
||||
current_chip_code = pending_user["chip_code"]
|
||||
current_card_code = pending_user["parking_card_code"]
|
||||
|
||||
if user is None and pending_user is None:
|
||||
pending_user = {
|
||||
"full_name": full_name,
|
||||
"chip_code": None,
|
||||
"parking_card_code": None,
|
||||
}
|
||||
operations.append(("user", pending_user))
|
||||
|
||||
if asset_code_in_use(db, asset_type, asset_code):
|
||||
assigned_to_same_user = (
|
||||
asset_type == "chip" and current_chip_code == asset_code
|
||||
) or (
|
||||
asset_type == "parking_card" and current_card_code == asset_code
|
||||
)
|
||||
if not assigned_to_same_user:
|
||||
errors.append(f"Zeile {index}: Kennung '{asset_code}' ist bereits vergeben.")
|
||||
continue
|
||||
|
||||
duplicate_in_import = False
|
||||
for operation_type, payload in operations:
|
||||
if operation_type != "assign":
|
||||
continue
|
||||
if payload["asset_type"] == asset_type and payload["asset_code"] == asset_code:
|
||||
same_user = payload["full_name"].lower() == full_name.lower()
|
||||
if not same_user:
|
||||
duplicate_in_import = True
|
||||
break
|
||||
if duplicate_in_import:
|
||||
errors.append(f"Zeile {index}: Kennung '{asset_code}' wird im Import mehrfach vergeben.")
|
||||
continue
|
||||
|
||||
if asset_type == "chip":
|
||||
if current_chip_code and current_chip_code != asset_code:
|
||||
errors.append(f"Zeile {index}: User '{full_name}' hat bereits einen anderen Tuerchip.")
|
||||
continue
|
||||
if pending_user is not None:
|
||||
pending_user["chip_code"] = asset_code
|
||||
else:
|
||||
if current_card_code and current_card_code != asset_code:
|
||||
errors.append(f"Zeile {index}: User '{full_name}' hat bereits eine andere Parkkarte.")
|
||||
continue
|
||||
if pending_user is not None:
|
||||
pending_user["parking_card_code"] = asset_code
|
||||
|
||||
operations.append(
|
||||
(
|
||||
"assign",
|
||||
{
|
||||
"full_name": full_name,
|
||||
"user_id": user_id,
|
||||
"asset_type": asset_type,
|
||||
"asset_code": asset_code,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
if errors:
|
||||
flash(errors, "import-errors")
|
||||
return redirect(url_for("import_data"))
|
||||
|
||||
user_ids_by_name: dict[str, int] = {}
|
||||
for operation_type, payload in operations:
|
||||
if operation_type == "user":
|
||||
db.execute(
|
||||
"INSERT INTO users (full_name) VALUES (?)",
|
||||
(payload["full_name"],),
|
||||
)
|
||||
user_ids_by_name[payload["full_name"].lower()] = db.execute(
|
||||
"SELECT last_insert_rowid() AS id"
|
||||
).fetchone()["id"]
|
||||
|
||||
for operation_type, payload in operations:
|
||||
if operation_type != "assign":
|
||||
continue
|
||||
|
||||
resolved_user_id = payload["user_id"] or user_ids_by_name[payload["full_name"].lower()]
|
||||
|
||||
if payload["asset_type"] == "chip":
|
||||
db.execute(
|
||||
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||
(payload["asset_code"], resolved_user_id),
|
||||
)
|
||||
else:
|
||||
db.execute(
|
||||
"UPDATE users SET parking_card_code = ?, parking_card_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||
(payload["asset_code"], resolved_user_id),
|
||||
)
|
||||
|
||||
db.execute(
|
||||
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'assign')",
|
||||
(resolved_user_id, payload["asset_type"], payload["asset_code"], "Import"),
|
||||
)
|
||||
log_asset_event("import", payload["full_name"], payload["asset_type"], payload["asset_code"], "Import")
|
||||
imported_count += 1
|
||||
|
||||
db.commit()
|
||||
flash(f"{imported_count} Importzeilen wurden verarbeitet.", "success")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
return render_template("import_data.html")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@login_required
|
||||
def index() -> str:
|
||||
db = get_db()
|
||||
search_query = request.args.get("q", "").strip()
|
||||
params: tuple[str, ...] = ()
|
||||
user_query = """
|
||||
SELECT id, full_name, email, department, chip_code, chip_assigned_at,
|
||||
parking_card_code, parking_card_assigned_at
|
||||
FROM users
|
||||
"""
|
||||
|
||||
if search_query:
|
||||
like_value = f"%{search_query}%"
|
||||
user_query += """
|
||||
WHERE full_name LIKE ?
|
||||
OR email LIKE ?
|
||||
OR chip_code LIKE ?
|
||||
OR parking_card_code LIKE ?
|
||||
"""
|
||||
params = (like_value, like_value, like_value, like_value)
|
||||
|
||||
user_query += " ORDER BY full_name COLLATE NOCASE"
|
||||
users = db.execute(user_query, params).fetchall()
|
||||
|
||||
stats = db.execute(
|
||||
"""
|
||||
SELECT
|
||||
COUNT(*) AS users,
|
||||
SUM(CASE WHEN chip_code IS NOT NULL AND chip_code != '' THEN 1 ELSE 0 END) AS active_chips,
|
||||
SUM(CASE WHEN parking_card_code IS NOT NULL AND parking_card_code != '' THEN 1 ELSE 0 END) AS active_cards
|
||||
FROM users
|
||||
"""
|
||||
).fetchone()
|
||||
|
||||
transactions = db.execute(
|
||||
"""
|
||||
SELECT t.id, t.created_at, t.asset_type, t.asset_code, t.action, t.handled_by, u.full_name
|
||||
FROM transactions t
|
||||
JOIN users u ON u.id = t.user_id
|
||||
ORDER BY t.created_at DESC, t.id DESC
|
||||
LIMIT 10
|
||||
"""
|
||||
).fetchall()
|
||||
|
||||
recent_logs = read_recent_logs()
|
||||
|
||||
return render_template(
|
||||
"index.html",
|
||||
users=users,
|
||||
transactions=transactions,
|
||||
stats={
|
||||
"users": stats["users"],
|
||||
"active_chips": stats["active_chips"],
|
||||
"active_cards": stats["active_cards"],
|
||||
},
|
||||
search_query=search_query,
|
||||
asset_labels=ASSET_LABELS,
|
||||
action_labels=ACTION_LABELS,
|
||||
recent_logs=recent_logs,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/users/new", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def create_user() -> str:
|
||||
if request.method == "POST":
|
||||
full_name = request.form.get("full_name", "").strip()
|
||||
email = request.form.get("email", "").strip() or None
|
||||
department = request.form.get("department", "").strip() or None
|
||||
|
||||
if not full_name:
|
||||
flash("Bitte einen Namen eingeben.")
|
||||
return redirect(url_for("create_user"))
|
||||
|
||||
db = get_db()
|
||||
db.execute(
|
||||
"INSERT INTO users (full_name, email, department) VALUES (?, ?, ?)",
|
||||
(full_name, email, department),
|
||||
)
|
||||
db.commit()
|
||||
flash("User wurde angelegt.")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
return render_template("create_user.html")
|
||||
|
||||
|
||||
@app.route("/assign", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def assign_asset() -> str:
|
||||
db = get_db()
|
||||
|
||||
if request.method == "POST":
|
||||
user_id = request.form.get("user_id", "").strip()
|
||||
asset_type = request.form.get("asset_type", "").strip()
|
||||
asset_code = request.form.get("asset_code", "").strip()
|
||||
handled_by = g.current_staff["full_name"]
|
||||
|
||||
if not user_id or asset_type not in {"chip", "parking_card"} or not asset_code:
|
||||
flash("Bitte alle Felder fuer die Ausgabe ausfuellen.")
|
||||
return redirect(url_for("assign_asset"))
|
||||
|
||||
user = db.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
|
||||
if user is None:
|
||||
flash("Ausgewaehlter User wurde nicht gefunden.")
|
||||
return redirect(url_for("assign_asset"))
|
||||
|
||||
if asset_code_in_use(db, asset_type, asset_code):
|
||||
flash("Diese Kennung ist bereits vergeben.")
|
||||
return redirect(url_for("assign_asset"))
|
||||
|
||||
if asset_type == "chip":
|
||||
if user["chip_code"]:
|
||||
flash("Dieser User hat bereits einen aktiven Tuerchip.")
|
||||
return redirect(url_for("assign_asset"))
|
||||
db.execute(
|
||||
"UPDATE users SET chip_code = ?, chip_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||
(asset_code, user_id),
|
||||
)
|
||||
else:
|
||||
if user["parking_card_code"]:
|
||||
flash("Dieser User hat bereits eine aktive Parkkarte.")
|
||||
return redirect(url_for("assign_asset"))
|
||||
db.execute(
|
||||
"UPDATE users SET parking_card_code = ?, parking_card_assigned_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||
(asset_code, user_id),
|
||||
)
|
||||
|
||||
db.execute(
|
||||
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'assign')",
|
||||
(user_id, asset_type, asset_code, handled_by),
|
||||
)
|
||||
transaction_id = db.execute("SELECT last_insert_rowid() AS id").fetchone()["id"]
|
||||
db.commit()
|
||||
log_asset_event("ausgabe", user["full_name"], asset_type, asset_code, handled_by)
|
||||
flash("Ausgabe wurde gespeichert.")
|
||||
return redirect(url_for("print_transaction", transaction_id=transaction_id))
|
||||
|
||||
users = db.execute("SELECT id, full_name FROM users ORDER BY full_name COLLATE NOCASE").fetchall()
|
||||
return render_template("assign_asset.html", users=users)
|
||||
|
||||
|
||||
@app.route("/return", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def return_asset() -> str:
|
||||
db = get_db()
|
||||
|
||||
if request.method == "POST":
|
||||
user_id = request.form.get("user_id", "").strip()
|
||||
asset_type = request.form.get("asset_type", "").strip()
|
||||
handled_by = g.current_staff["full_name"]
|
||||
|
||||
if not user_id or asset_type not in {"chip", "parking_card"}:
|
||||
flash("Bitte User und Typ fuer die Rueckgabe waehlen.")
|
||||
return redirect(url_for("return_asset"))
|
||||
|
||||
user = db.execute("SELECT * FROM users WHERE id = ?", (user_id,)).fetchone()
|
||||
if user is None:
|
||||
flash("Ausgewaehlter User wurde nicht gefunden.")
|
||||
return redirect(url_for("return_asset"))
|
||||
|
||||
asset_code = user["chip_code"] if asset_type == "chip" else user["parking_card_code"]
|
||||
if not asset_code:
|
||||
flash("Fuer diesen User ist kein entsprechendes Medium aktiv hinterlegt.")
|
||||
return redirect(url_for("return_asset"))
|
||||
|
||||
if asset_type == "chip":
|
||||
db.execute(
|
||||
"UPDATE users SET chip_code = NULL, chip_assigned_at = NULL WHERE id = ?",
|
||||
(user_id,),
|
||||
)
|
||||
else:
|
||||
db.execute(
|
||||
"UPDATE users SET parking_card_code = NULL, parking_card_assigned_at = NULL WHERE id = ?",
|
||||
(user_id,),
|
||||
)
|
||||
|
||||
db.execute(
|
||||
"INSERT INTO transactions (user_id, asset_type, asset_code, handled_by, action) VALUES (?, ?, ?, ?, 'return')",
|
||||
(user_id, asset_type, asset_code, handled_by),
|
||||
)
|
||||
transaction_id = db.execute("SELECT last_insert_rowid() AS id").fetchone()["id"]
|
||||
db.commit()
|
||||
log_asset_event("rueckgabe", user["full_name"], asset_type, asset_code, handled_by)
|
||||
flash("Rueckgabe wurde gespeichert.")
|
||||
return redirect(url_for("print_transaction", transaction_id=transaction_id))
|
||||
|
||||
users = db.execute("SELECT id, full_name FROM users ORDER BY full_name COLLATE NOCASE").fetchall()
|
||||
return render_template("return_asset.html", users=users)
|
||||
|
||||
|
||||
@app.route("/transactions/<int:transaction_id>/print")
|
||||
@login_required
|
||||
def print_transaction(transaction_id: int) -> str:
|
||||
transaction = get_transaction_for_print(transaction_id)
|
||||
if transaction is None:
|
||||
flash("Druckbeleg wurde nicht gefunden.")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
return render_template(
|
||||
"print_transaction.html",
|
||||
transaction=transaction,
|
||||
asset_labels=ASSET_LABELS,
|
||||
action_labels=ACTION_LABELS,
|
||||
print_descriptions=PRINT_DESCRIPTIONS,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
services:
|
||||
keyVerwaltung:
|
||||
container_name: keyVerwaltung
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
platforms:
|
||||
- linux/amd64
|
||||
image: gitea.teamthiele.de/ethiele/keyVerwaltung:latest
|
||||
ports:
|
||||
- "5006:5006"
|
||||
restart: unless-stopped
|
||||
BIN
inventory.db
Normal file
BIN
inventory.db
Normal file
Binary file not shown.
709
inventory.log
Normal file
709
inventory.log
Normal file
@@ -0,0 +1,709 @@
|
||||
2026-05-18 14:23:42,463 WARNING * Debugger is active!
|
||||
2026-05-18 14:23:42,491 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 14:25:32,695 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 14:27:46,631 INFO [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||
* Running on http://127.0.0.1:5000
|
||||
2026-05-18 14:27:46,631 INFO [33mPress CTRL+C to quit[0m
|
||||
2026-05-18 14:27:46,632 INFO * Restarting with stat
|
||||
2026-05-18 14:27:46,834 WARNING * Debugger is active!
|
||||
2026-05-18 14:27:46,851 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 14:28:03,048 INFO [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||
* Running on http://127.0.0.1:5000
|
||||
2026-05-18 14:28:03,048 INFO [33mPress CTRL+C to quit[0m
|
||||
2026-05-18 14:28:03,049 INFO * Restarting with stat
|
||||
2026-05-18 14:28:03,246 WARNING * Debugger is active!
|
||||
2026-05-18 14:28:03,261 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 14:28:27,475 INFO 127.0.0.1 - - [18/May/2026 14:28:27] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:28:59,158 INFO 127.0.0.1 - - [18/May/2026 14:28:59] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:29:00,812 INFO 127.0.0.1 - - [18/May/2026 14:29:00] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:29:16,461 INFO 127.0.0.1 - - [18/May/2026 14:29:16] "[32mPOST /users/new HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:29:16,478 INFO 127.0.0.1 - - [18/May/2026 14:29:16] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:29:24,608 INFO 127.0.0.1 - - [18/May/2026 14:29:24] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 14:29:56,152 INFO ausgabe | user=Erik Thiele | typ=Tuerchip | kennung=120 | bearbeiter=Erik Thiele
|
||||
2026-05-18 14:29:56,153 INFO 127.0.0.1 - - [18/May/2026 14:29:56] "[32mPOST /assign HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:29:56,172 INFO 127.0.0.1 - - [18/May/2026 14:29:56] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:30:07,243 INFO 127.0.0.1 - - [18/May/2026 14:30:07] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 14:30:24,830 INFO ausgabe | user=Erik Thiele | typ=Parkkarte | kennung=1234567 | bearbeiter=Erik Thiele
|
||||
2026-05-18 14:30:24,831 INFO 127.0.0.1 - - [18/May/2026 14:30:24] "[32mPOST /assign HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:30:24,857 INFO 127.0.0.1 - - [18/May/2026 14:30:24] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:31:04,359 INFO 127.0.0.1 - - [18/May/2026 14:31:04] "GET /?q=erik HTTP/1.1" 200 -
|
||||
2026-05-18 14:31:17,431 INFO 127.0.0.1 - - [18/May/2026 14:31:17] "GET /?q=erik HTTP/1.1" 200 -
|
||||
2026-05-18 14:31:27,322 INFO 127.0.0.1 - - [18/May/2026 14:31:27] "GET /?q=120 HTTP/1.1" 200 -
|
||||
2026-05-18 14:31:30,855 INFO 127.0.0.1 - - [18/May/2026 14:31:30] "GET /?q=120 HTTP/1.1" 200 -
|
||||
2026-05-18 14:31:35,341 INFO 127.0.0.1 - - [18/May/2026 14:31:35] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:36:58,604 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 14:36:58,639 INFO * Restarting with stat
|
||||
2026-05-18 14:36:59,593 WARNING * Debugger is active!
|
||||
2026-05-18 14:36:59,623 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 14:38:46,585 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 14:38:46,618 INFO * Restarting with stat
|
||||
2026-05-18 14:38:46,951 WARNING * Debugger is active!
|
||||
2026-05-18 14:38:46,971 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 14:39:43,527 INFO 127.0.0.1 - - [18/May/2026 14:39:43] "[32mGET / HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:39:43,551 INFO 127.0.0.1 - - [18/May/2026 14:39:43] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 14:39:52,116 INFO 127.0.0.1 - - [18/May/2026 14:39:52] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:39:52,122 INFO 127.0.0.1 - - [18/May/2026 14:39:52] "GET /set-password HTTP/1.1" 200 -
|
||||
2026-05-18 14:40:04,983 INFO 127.0.0.1 - - [18/May/2026 14:40:04] "[32mPOST /set-password HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:40:05,015 INFO 127.0.0.1 - - [18/May/2026 14:40:05] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:40:20,652 INFO 127.0.0.1 - - [18/May/2026 14:40:20] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:40:21,757 INFO 127.0.0.1 - - [18/May/2026 14:40:21] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:40:24,177 INFO 127.0.0.1 - - [18/May/2026 14:40:24] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 14:40:25,515 INFO 127.0.0.1 - - [18/May/2026 14:40:25] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 14:40:26,507 INFO 127.0.0.1 - - [18/May/2026 14:40:26] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 14:43:46,955 INFO 127.0.0.1 - - [18/May/2026 14:43:46] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:45:26,947 INFO 127.0.0.1 - - [18/May/2026 14:45:26] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:22,094 INFO 127.0.0.1 - - [18/May/2026 14:51:22] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:22,134 INFO 127.0.0.1 - - [18/May/2026 14:51:22] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:22,139 INFO 127.0.0.1 - - [18/May/2026 14:51:22] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:27,874 INFO 127.0.0.1 - - [18/May/2026 14:51:27] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:27,897 INFO 127.0.0.1 - - [18/May/2026 14:51:27] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:51:29,057 INFO 127.0.0.1 - - [18/May/2026 14:51:29] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:29,080 INFO 127.0.0.1 - - [18/May/2026 14:51:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:51:29,947 INFO 127.0.0.1 - - [18/May/2026 14:51:29] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:29,979 INFO 127.0.0.1 - - [18/May/2026 14:51:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:51:30,825 INFO 127.0.0.1 - - [18/May/2026 14:51:30] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:30,857 INFO 127.0.0.1 - - [18/May/2026 14:51:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:51:53,088 INFO 127.0.0.1 - - [18/May/2026 14:51:53] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:53,121 INFO 127.0.0.1 - - [18/May/2026 14:51:53] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:51:55,405 INFO 127.0.0.1 - - [18/May/2026 14:51:55] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:55,431 INFO 127.0.0.1 - - [18/May/2026 14:51:55] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:51:58,704 INFO 127.0.0.1 - - [18/May/2026 14:51:58] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:58,726 INFO 127.0.0.1 - - [18/May/2026 14:51:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:51:59,901 INFO 127.0.0.1 - - [18/May/2026 14:51:59] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 14:51:59,934 INFO 127.0.0.1 - - [18/May/2026 14:51:59] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:52:01,807 INFO 127.0.0.1 - - [18/May/2026 14:52:01] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 14:52:01,828 INFO 127.0.0.1 - - [18/May/2026 14:52:01] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:52:09,060 INFO 127.0.0.1 - - [18/May/2026 14:52:09] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:52:09,091 INFO 127.0.0.1 - - [18/May/2026 14:52:09] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:52:18,275 INFO 127.0.0.1 - - [18/May/2026 14:52:18] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:52:18,286 INFO 127.0.0.1 - - [18/May/2026 14:52:18] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 14:52:18,306 INFO 127.0.0.1 - - [18/May/2026 14:52:18] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:52:18,343 INFO 127.0.0.1 - - [18/May/2026 14:52:18] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:52:24,523 INFO 127.0.0.1 - - [18/May/2026 14:52:24] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 14:52:24,543 INFO 127.0.0.1 - - [18/May/2026 14:52:24] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:53:35,342 INFO 127.0.0.1 - - [18/May/2026 14:53:35] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 14:53:35,372 INFO 127.0.0.1 - - [18/May/2026 14:53:35] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:54:51,498 INFO 127.0.0.1 - - [18/May/2026 14:54:51] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 14:54:51,521 INFO 127.0.0.1 - - [18/May/2026 14:54:51] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:55:01,447 INFO 127.0.0.1 - - [18/May/2026 14:55:01] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 14:55:01,472 INFO 127.0.0.1 - - [18/May/2026 14:55:01] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:55:05,929 INFO 127.0.0.1 - - [18/May/2026 14:55:05] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 14:55:05,955 INFO 127.0.0.1 - - [18/May/2026 14:55:05] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:55:06,030 INFO 127.0.0.1 - - [18/May/2026 14:55:06] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:55:06,039 INFO 127.0.0.1 - - [18/May/2026 14:55:06] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:56:04,001 INFO 127.0.0.1 - - [18/May/2026 14:56:04] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:56:04,038 INFO 127.0.0.1 - - [18/May/2026 14:56:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:56:05,926 INFO 127.0.0.1 - - [18/May/2026 14:56:05] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 14:56:05,948 INFO 127.0.0.1 - - [18/May/2026 14:56:05] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:56:07,459 INFO 127.0.0.1 - - [18/May/2026 14:56:07] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 14:56:07,482 INFO 127.0.0.1 - - [18/May/2026 14:56:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:56:09,495 INFO 127.0.0.1 - - [18/May/2026 14:56:09] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 14:56:09,528 INFO 127.0.0.1 - - [18/May/2026 14:56:09] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:56:12,825 INFO 127.0.0.1 - - [18/May/2026 14:56:12] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:56:12,846 INFO 127.0.0.1 - - [18/May/2026 14:56:12] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:11,098 INFO 127.0.0.1 - - [18/May/2026 14:58:11] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:11,121 INFO 127.0.0.1 - - [18/May/2026 14:58:11] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:12,620 INFO 127.0.0.1 - - [18/May/2026 14:58:12] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:12,644 INFO 127.0.0.1 - - [18/May/2026 14:58:12] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:15,051 INFO 127.0.0.1 - - [18/May/2026 14:58:15] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:15,070 INFO 127.0.0.1 - - [18/May/2026 14:58:15] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:16,525 INFO 127.0.0.1 - - [18/May/2026 14:58:16] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:16,557 INFO 127.0.0.1 - - [18/May/2026 14:58:16] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:17,670 INFO 127.0.0.1 - - [18/May/2026 14:58:17] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:17,694 INFO 127.0.0.1 - - [18/May/2026 14:58:17] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:18,850 INFO 127.0.0.1 - - [18/May/2026 14:58:18] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:18,872 INFO 127.0.0.1 - - [18/May/2026 14:58:18] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:21,180 INFO 127.0.0.1 - - [18/May/2026 14:58:21] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:21,205 INFO 127.0.0.1 - - [18/May/2026 14:58:21] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:24,907 INFO 127.0.0.1 - - [18/May/2026 14:58:24] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:25,076 INFO 127.0.0.1 - - [18/May/2026 14:58:25] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:25,082 INFO 127.0.0.1 - - [18/May/2026 14:58:25] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:26,874 INFO 127.0.0.1 - - [18/May/2026 14:58:26] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:26,894 INFO 127.0.0.1 - - [18/May/2026 14:58:26] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:58:28,238 INFO 127.0.0.1 - - [18/May/2026 14:58:28] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:28,368 INFO 127.0.0.1 - - [18/May/2026 14:58:28] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:28,372 INFO 127.0.0.1 - - [18/May/2026 14:58:28] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:30,860 INFO 127.0.0.1 - - [18/May/2026 14:58:30] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 14:58:30,879 INFO 127.0.0.1 - - [18/May/2026 14:58:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 14:59:55,091 INFO [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||
* Running on http://127.0.0.1:5000
|
||||
2026-05-18 14:59:55,092 INFO [33mPress CTRL+C to quit[0m
|
||||
2026-05-18 14:59:55,095 INFO * Restarting with stat
|
||||
2026-05-18 14:59:55,306 WARNING * Debugger is active!
|
||||
2026-05-18 14:59:55,325 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 15:00:03,755 INFO 127.0.0.1 - - [18/May/2026 15:00:03] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:03,781 INFO 127.0.0.1 - - [18/May/2026 15:00:03] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:06,303 INFO 127.0.0.1 - - [18/May/2026 15:00:06] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:06,326 INFO 127.0.0.1 - - [18/May/2026 15:00:06] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:08,982 INFO 127.0.0.1 - - [18/May/2026 15:00:08] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:09,001 INFO 127.0.0.1 - - [18/May/2026 15:00:09] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:10,975 INFO 127.0.0.1 - - [18/May/2026 15:00:10] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:11,004 INFO 127.0.0.1 - - [18/May/2026 15:00:11] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:12,451 INFO 127.0.0.1 - - [18/May/2026 15:00:12] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:12,473 INFO 127.0.0.1 - - [18/May/2026 15:00:12] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:13,662 INFO 127.0.0.1 - - [18/May/2026 15:00:13] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:13,689 INFO 127.0.0.1 - - [18/May/2026 15:00:13] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:15,242 INFO 127.0.0.1 - - [18/May/2026 15:00:15] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:15,275 INFO 127.0.0.1 - - [18/May/2026 15:00:15] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:17,438 INFO 127.0.0.1 - - [18/May/2026 15:00:17] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:17,458 INFO 127.0.0.1 - - [18/May/2026 15:00:17] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:22,487 INFO 127.0.0.1 - - [18/May/2026 15:00:22] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:22,522 INFO 127.0.0.1 - - [18/May/2026 15:00:22] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:28,656 INFO 127.0.0.1 - - [18/May/2026 15:00:28] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:28,787 INFO 127.0.0.1 - - [18/May/2026 15:00:28] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:28,791 INFO 127.0.0.1 - - [18/May/2026 15:00:28] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:30,396 INFO 127.0.0.1 - - [18/May/2026 15:00:30] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:30,422 INFO 127.0.0.1 - - [18/May/2026 15:00:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:33,657 INFO 127.0.0.1 - - [18/May/2026 15:00:33] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:33,677 INFO 127.0.0.1 - - [18/May/2026 15:00:33] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:35,094 INFO 127.0.0.1 - - [18/May/2026 15:00:35] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:35,111 INFO 127.0.0.1 - - [18/May/2026 15:00:35] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:51,171 INFO 127.0.0.1 - - [18/May/2026 15:00:51] "[32mGET / HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:00:51,175 INFO 127.0.0.1 - - [18/May/2026 15:00:51] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:51,312 INFO 127.0.0.1 - - [18/May/2026 15:00:51] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:51,316 INFO 127.0.0.1 - - [18/May/2026 15:00:51] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:58,718 INFO 127.0.0.1 - - [18/May/2026 15:00:58] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:00:58,748 INFO 127.0.0.1 - - [18/May/2026 15:00:58] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:00:58,779 INFO 127.0.0.1 - - [18/May/2026 15:00:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:00:58,784 INFO 127.0.0.1 - - [18/May/2026 15:00:58] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:01:01,021 INFO 127.0.0.1 - - [18/May/2026 15:01:01] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:01:01,043 INFO 127.0.0.1 - - [18/May/2026 15:01:01] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:01:02,178 INFO 127.0.0.1 - - [18/May/2026 15:01:02] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:01:02,195 INFO 127.0.0.1 - - [18/May/2026 15:01:02] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:18,767 INFO 127.0.0.1 - - [18/May/2026 15:02:18] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:18,790 INFO 127.0.0.1 - - [18/May/2026 15:02:18] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:20,657 INFO 127.0.0.1 - - [18/May/2026 15:02:20] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:20,680 INFO 127.0.0.1 - - [18/May/2026 15:02:20] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:24,698 INFO 127.0.0.1 - - [18/May/2026 15:02:24] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:24,853 INFO 127.0.0.1 - - [18/May/2026 15:02:24] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:24,857 INFO 127.0.0.1 - - [18/May/2026 15:02:24] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:28,193 INFO 127.0.0.1 - - [18/May/2026 15:02:28] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:28,224 INFO 127.0.0.1 - - [18/May/2026 15:02:28] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:29,573 INFO 127.0.0.1 - - [18/May/2026 15:02:29] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:29,594 INFO 127.0.0.1 - - [18/May/2026 15:02:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:30,264 INFO 127.0.0.1 - - [18/May/2026 15:02:30] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:30,291 INFO 127.0.0.1 - - [18/May/2026 15:02:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:32,218 INFO 127.0.0.1 - - [18/May/2026 15:02:32] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:32,238 INFO 127.0.0.1 - - [18/May/2026 15:02:32] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:33,403 INFO 127.0.0.1 - - [18/May/2026 15:02:33] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:33,426 INFO 127.0.0.1 - - [18/May/2026 15:02:33] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:02:34,928 INFO 127.0.0.1 - - [18/May/2026 15:02:34] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:02:34,945 INFO 127.0.0.1 - - [18/May/2026 15:02:34] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:03:52,750 INFO 127.0.0.1 - - [18/May/2026 15:03:52] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:03:52,772 INFO 127.0.0.1 - - [18/May/2026 15:03:52] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:03:57,589 INFO 127.0.0.1 - - [18/May/2026 15:03:57] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:03:57,612 INFO 127.0.0.1 - - [18/May/2026 15:03:57] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:03:58,815 INFO 127.0.0.1 - - [18/May/2026 15:03:58] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:03:58,846 INFO 127.0.0.1 - - [18/May/2026 15:03:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:29,725 INFO 127.0.0.1 - - [18/May/2026 15:04:29] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:04:29,745 INFO 127.0.0.1 - - [18/May/2026 15:04:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:37,230 INFO 127.0.0.1 - - [18/May/2026 15:04:37] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:04:37,254 INFO 127.0.0.1 - - [18/May/2026 15:04:37] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:38,585 INFO 127.0.0.1 - - [18/May/2026 15:04:38] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:04:38,610 INFO 127.0.0.1 - - [18/May/2026 15:04:38] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:44,172 INFO 127.0.0.1 - - [18/May/2026 15:04:44] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:04:44,193 INFO 127.0.0.1 - - [18/May/2026 15:04:44] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:45,202 INFO 127.0.0.1 - - [18/May/2026 15:04:45] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:04:45,225 INFO 127.0.0.1 - - [18/May/2026 15:04:45] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:53,743 INFO 127.0.0.1 - - [18/May/2026 15:04:53] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:04:53,753 INFO 127.0.0.1 - - [18/May/2026 15:04:53] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 15:04:53,776 INFO 127.0.0.1 - - [18/May/2026 15:04:53] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:53,805 INFO 127.0.0.1 - - [18/May/2026 15:04:53] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:57,201 INFO 127.0.0.1 - - [18/May/2026 15:04:57] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:04:57,231 INFO 127.0.0.1 - - [18/May/2026 15:04:57] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:04:57,255 INFO 127.0.0.1 - - [18/May/2026 15:04:57] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:04:57,262 INFO 127.0.0.1 - - [18/May/2026 15:04:57] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:05:00,054 INFO 127.0.0.1 - - [18/May/2026 15:05:00] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:05:00,063 INFO 127.0.0.1 - - [18/May/2026 15:05:00] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 15:05:00,083 INFO 127.0.0.1 - - [18/May/2026 15:05:00] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:05:00,106 INFO 127.0.0.1 - - [18/May/2026 15:05:00] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:05:05,077 INFO 127.0.0.1 - - [18/May/2026 15:05:05] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:05:05,109 INFO 127.0.0.1 - - [18/May/2026 15:05:05] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:05:05,138 INFO 127.0.0.1 - - [18/May/2026 15:05:05] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:05:05,143 INFO 127.0.0.1 - - [18/May/2026 15:05:05] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:05:13,575 INFO 127.0.0.1 - - [18/May/2026 15:05:13] "GET /?q= HTTP/1.1" 200 -
|
||||
2026-05-18 15:05:13,598 INFO 127.0.0.1 - - [18/May/2026 15:05:13] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:05:33,511 INFO 127.0.0.1 - - [18/May/2026 15:05:33] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:05:33,548 INFO 127.0.0.1 - - [18/May/2026 15:05:33] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:05:38,642 INFO 127.0.0.1 - - [18/May/2026 15:05:38] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:05:38,680 INFO 127.0.0.1 - - [18/May/2026 15:05:38] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:06:39,481 INFO 127.0.0.1 - - [18/May/2026 15:06:39] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:06:39,593 INFO 127.0.0.1 - - [18/May/2026 15:06:39] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:07:42,446 INFO 127.0.0.1 - - [18/May/2026 15:07:42] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:07:42,472 INFO 127.0.0.1 - - [18/May/2026 15:07:42] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:08:04,884 INFO 127.0.0.1 - - [18/May/2026 15:08:04] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 15:08:04,914 INFO 127.0.0.1 - - [18/May/2026 15:08:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:08:16,423 INFO rueckgabe | user=Erik Thiele | typ=Parkkarte | kennung=1234567 | bearbeiter=Administrator
|
||||
2026-05-18 15:08:16,424 INFO 127.0.0.1 - - [18/May/2026 15:08:16] "[32mPOST /return HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:08:16,454 INFO 127.0.0.1 - - [18/May/2026 15:08:16] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:08:16,478 INFO 127.0.0.1 - - [18/May/2026 15:08:16] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:08:16,491 INFO 127.0.0.1 - - [18/May/2026 15:08:16] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:08:33,800 INFO 127.0.0.1 - - [18/May/2026 15:08:33] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:08:33,832 INFO 127.0.0.1 - - [18/May/2026 15:08:33] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:08:42,613 INFO ausgabe | user=Erik Thiele | typ=Parkkarte | kennung=122223333 | bearbeiter=Administrator
|
||||
2026-05-18 15:08:42,614 INFO 127.0.0.1 - - [18/May/2026 15:08:42] "[32mPOST /assign HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:08:42,647 INFO 127.0.0.1 - - [18/May/2026 15:08:42] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:08:42,678 INFO 127.0.0.1 - - [18/May/2026 15:08:42] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:08:42,684 INFO 127.0.0.1 - - [18/May/2026 15:08:42] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:10:40,947 INFO 127.0.0.1 - - [18/May/2026 15:10:40] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:10:40,970 INFO 127.0.0.1 - - [18/May/2026 15:10:40] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:12:29,420 INFO 127.0.0.1 - - [18/May/2026 15:12:29] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:29,443 INFO 127.0.0.1 - - [18/May/2026 15:12:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:12:42,198 INFO 127.0.0.1 - - [18/May/2026 15:12:42] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:42,365 INFO 127.0.0.1 - - [18/May/2026 15:12:42] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:42,371 INFO 127.0.0.1 - - [18/May/2026 15:12:42] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:43,719 INFO 127.0.0.1 - - [18/May/2026 15:12:43] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:43,843 INFO 127.0.0.1 - - [18/May/2026 15:12:43] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:43,846 INFO 127.0.0.1 - - [18/May/2026 15:12:43] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:44,053 INFO 127.0.0.1 - - [18/May/2026 15:12:44] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:44,206 INFO 127.0.0.1 - - [18/May/2026 15:12:44] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:45,479 INFO 127.0.0.1 - - [18/May/2026 15:12:45] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:45,606 INFO 127.0.0.1 - - [18/May/2026 15:12:45] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:45,610 INFO 127.0.0.1 - - [18/May/2026 15:12:45] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:45,817 INFO 127.0.0.1 - - [18/May/2026 15:12:45] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:45,971 INFO 127.0.0.1 - - [18/May/2026 15:12:45] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:47,080 INFO 127.0.0.1 - - [18/May/2026 15:12:47] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:47,268 INFO 127.0.0.1 - - [18/May/2026 15:12:47] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:47,278 INFO 127.0.0.1 - - [18/May/2026 15:12:47] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:48,638 INFO 127.0.0.1 - - [18/May/2026 15:12:48] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:12:48,656 INFO 127.0.0.1 - - [18/May/2026 15:12:48] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:13:04,246 INFO 127.0.0.1 - - [18/May/2026 15:13:04] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:13:04,272 INFO 127.0.0.1 - - [18/May/2026 15:13:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:13:12,342 INFO 127.0.0.1 - - [18/May/2026 15:13:12] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:13:12,370 INFO 127.0.0.1 - - [18/May/2026 15:13:12] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:13:18,031 INFO 127.0.0.1 - - [18/May/2026 15:13:18] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 15:13:18,054 INFO 127.0.0.1 - - [18/May/2026 15:13:18] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:13:20,777 INFO 127.0.0.1 - - [18/May/2026 15:13:20] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:13:20,801 INFO 127.0.0.1 - - [18/May/2026 15:13:20] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:13:24,403 INFO 127.0.0.1 - - [18/May/2026 15:13:24] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:13:24,431 INFO 127.0.0.1 - - [18/May/2026 15:13:24] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:14:42,665 INFO 127.0.0.1 - - [18/May/2026 15:14:42] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:14:42,687 INFO 127.0.0.1 - - [18/May/2026 15:14:42] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:16:21,627 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 15:16:21,670 INFO * Restarting with stat
|
||||
2026-05-18 15:16:22,024 WARNING * Debugger is active!
|
||||
2026-05-18 15:16:22,044 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 15:16:32,774 INFO 127.0.0.1 - - [18/May/2026 15:16:32] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:16:32,800 INFO 127.0.0.1 - - [18/May/2026 15:16:32] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:18:03,982 INFO 127.0.0.1 - - [18/May/2026 15:18:03] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:18:04,004 INFO 127.0.0.1 - - [18/May/2026 15:18:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:18:10,610 INFO 127.0.0.1 - - [18/May/2026 15:18:10] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:18:10,641 INFO 127.0.0.1 - - [18/May/2026 15:18:10] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:18:27,254 INFO 127.0.0.1 - - [18/May/2026 15:18:27] "[32mPOST /admin/staff HTTP/1.1[0m" 302 -
|
||||
2026-05-18 15:18:27,263 INFO 127.0.0.1 - - [18/May/2026 15:18:27] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:18:27,286 INFO 127.0.0.1 - - [18/May/2026 15:18:27] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:18:27,302 INFO 127.0.0.1 - - [18/May/2026 15:18:27] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:18:44,742 INFO 127.0.0.1 - - [18/May/2026 15:18:44] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:18:44,764 INFO 127.0.0.1 - - [18/May/2026 15:18:44] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:19:58,673 INFO 127.0.0.1 - - [18/May/2026 15:19:58] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:19:58,695 INFO 127.0.0.1 - - [18/May/2026 15:19:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:20:00,485 INFO 127.0.0.1 - - [18/May/2026 15:20:00] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:20:00,507 INFO 127.0.0.1 - - [18/May/2026 15:20:00] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:20:05,565 INFO 127.0.0.1 - - [18/May/2026 15:20:05] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:20:05,589 INFO 127.0.0.1 - - [18/May/2026 15:20:05] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:21:52,561 INFO 127.0.0.1 - - [18/May/2026 15:21:52] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:21:52,588 INFO 127.0.0.1 - - [18/May/2026 15:21:52] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:22:04,902 INFO 127.0.0.1 - - [18/May/2026 15:22:04] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:22:04,930 INFO 127.0.0.1 - - [18/May/2026 15:22:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:22:06,242 INFO 127.0.0.1 - - [18/May/2026 15:22:06] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:22:06,275 INFO 127.0.0.1 - - [18/May/2026 15:22:06] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:22:07,739 INFO 127.0.0.1 - - [18/May/2026 15:22:07] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 15:22:07,760 INFO 127.0.0.1 - - [18/May/2026 15:22:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:22:08,924 INFO 127.0.0.1 - - [18/May/2026 15:22:08] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:22:08,955 INFO 127.0.0.1 - - [18/May/2026 15:22:08] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:22:23,933 INFO 127.0.0.1 - - [18/May/2026 15:22:23] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:22:23,958 INFO 127.0.0.1 - - [18/May/2026 15:22:23] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:23:34,772 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 15:23:34,815 INFO * Restarting with stat
|
||||
2026-05-18 15:23:35,156 WARNING * Debugger is active!
|
||||
2026-05-18 15:23:35,176 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 15:23:53,582 INFO 127.0.0.1 - - [18/May/2026 15:23:53] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:23:53,608 INFO 127.0.0.1 - - [18/May/2026 15:23:53] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:24:00,814 INFO 127.0.0.1 - - [18/May/2026 15:24:00] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:24:00,840 INFO 127.0.0.1 - - [18/May/2026 15:24:00] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:25:09,784 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 15:25:09,817 INFO * Restarting with stat
|
||||
2026-05-18 15:25:10,185 WARNING * Debugger is active!
|
||||
2026-05-18 15:25:10,203 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 15:27:35,140 INFO 127.0.0.1 - - [18/May/2026 15:27:35] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:27:35,167 INFO 127.0.0.1 - - [18/May/2026 15:27:35] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:57:58,313 INFO 127.0.0.1 - - [18/May/2026 15:57:58] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:57:58,364 INFO 127.0.0.1 - - [18/May/2026 15:57:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:57:59,454 INFO 127.0.0.1 - - [18/May/2026 15:57:59] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:57:59,484 INFO 127.0.0.1 - - [18/May/2026 15:57:59] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:58:02,881 INFO 127.0.0.1 - - [18/May/2026 15:58:02] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 15:58:02,915 INFO 127.0.0.1 - - [18/May/2026 15:58:02] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:58:04,180 INFO 127.0.0.1 - - [18/May/2026 15:58:04] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 15:58:04,207 INFO 127.0.0.1 - - [18/May/2026 15:58:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:58:05,201 INFO 127.0.0.1 - - [18/May/2026 15:58:05] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 15:58:05,227 INFO 127.0.0.1 - - [18/May/2026 15:58:05] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:58:06,248 INFO 127.0.0.1 - - [18/May/2026 15:58:06] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 15:58:06,280 INFO 127.0.0.1 - - [18/May/2026 15:58:06] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 15:58:07,472 INFO 127.0.0.1 - - [18/May/2026 15:58:07] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 15:58:07,498 INFO 127.0.0.1 - - [18/May/2026 15:58:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:02:15,812 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 16:02:15,854 INFO * Restarting with stat
|
||||
2026-05-18 16:02:17,699 WARNING * Debugger is active!
|
||||
2026-05-18 16:02:17,738 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 16:02:25,166 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 16:02:25,202 INFO * Restarting with stat
|
||||
2026-05-18 16:02:25,462 WARNING * Debugger is active!
|
||||
2026-05-18 16:02:25,478 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 16:03:10,809 INFO 127.0.0.1 - - [18/May/2026 16:03:10] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:03:10,847 INFO 127.0.0.1 - - [18/May/2026 16:03:10] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:03:19,126 INFO 127.0.0.1 - - [18/May/2026 16:03:19] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:03:19,154 INFO 127.0.0.1 - - [18/May/2026 16:03:19] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:03:39,945 INFO 127.0.0.1 - - [18/May/2026 16:03:39] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:03:39,973 INFO 127.0.0.1 - - [18/May/2026 16:03:39] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:03:45,477 INFO 127.0.0.1 - - [18/May/2026 16:03:45] "GET /transactions/1/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:03:45,498 INFO 127.0.0.1 - - [18/May/2026 16:03:45] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:03:52,788 INFO 127.0.0.1 - - [18/May/2026 16:03:52] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:03:52,811 INFO 127.0.0.1 - - [18/May/2026 16:03:52] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:06:56,872 INFO 127.0.0.1 - - [18/May/2026 16:06:56] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 16:06:56,906 INFO 127.0.0.1 - - [18/May/2026 16:06:56] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:06:59,528 INFO 127.0.0.1 - - [18/May/2026 16:06:59] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 16:06:59,553 INFO 127.0.0.1 - - [18/May/2026 16:06:59] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:02,206 INFO 127.0.0.1 - - [18/May/2026 16:07:02] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 16:07:02,227 INFO 127.0.0.1 - - [18/May/2026 16:07:02] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:04,653 INFO 127.0.0.1 - - [18/May/2026 16:07:04] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 16:07:04,692 INFO 127.0.0.1 - - [18/May/2026 16:07:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:08,124 INFO 127.0.0.1 - - [18/May/2026 16:07:08] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:07:08,155 INFO 127.0.0.1 - - [18/May/2026 16:07:08] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:14,597 INFO 127.0.0.1 - - [18/May/2026 16:07:14] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:07:14,608 INFO 127.0.0.1 - - [18/May/2026 16:07:14] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:07:14,638 INFO 127.0.0.1 - - [18/May/2026 16:07:14] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:14,643 INFO 127.0.0.1 - - [18/May/2026 16:07:14] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:21,945 INFO 127.0.0.1 - - [18/May/2026 16:07:21] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:07:21,962 INFO 127.0.0.1 - - [18/May/2026 16:07:21] "GET /set-password HTTP/1.1" 200 -
|
||||
2026-05-18 16:07:22,077 INFO 127.0.0.1 - - [18/May/2026 16:07:22] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:22,090 INFO 127.0.0.1 - - [18/May/2026 16:07:22] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:30,789 INFO 127.0.0.1 - - [18/May/2026 16:07:30] "[32mPOST /set-password HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:07:30,820 INFO 127.0.0.1 - - [18/May/2026 16:07:30] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:07:30,858 INFO 127.0.0.1 - - [18/May/2026 16:07:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:07:30,868 INFO 127.0.0.1 - - [18/May/2026 16:07:30] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:08:12,289 INFO 127.0.0.1 - - [18/May/2026 16:08:12] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:08:12,319 INFO 127.0.0.1 - - [18/May/2026 16:08:12] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:08:17,486 INFO 127.0.0.1 - - [18/May/2026 16:08:17] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 16:08:17,508 INFO 127.0.0.1 - - [18/May/2026 16:08:17] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:08:18,649 INFO 127.0.0.1 - - [18/May/2026 16:08:18] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 16:08:18,671 INFO 127.0.0.1 - - [18/May/2026 16:08:18] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:08:19,472 INFO 127.0.0.1 - - [18/May/2026 16:08:19] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 16:08:19,504 INFO 127.0.0.1 - - [18/May/2026 16:08:19] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:08:20,905 INFO 127.0.0.1 - - [18/May/2026 16:08:20] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:08:20,936 INFO 127.0.0.1 - - [18/May/2026 16:08:20] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:10:20,468 INFO 127.0.0.1 - - [18/May/2026 16:10:20] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:10:20,479 INFO 127.0.0.1 - - [18/May/2026 16:10:20] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:10:20,505 INFO 127.0.0.1 - - [18/May/2026 16:10:20] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:10:20,524 INFO 127.0.0.1 - - [18/May/2026 16:10:20] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:12:56,071 INFO 127.0.0.1 - - [18/May/2026 16:12:56] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:12:56,094 INFO 127.0.0.1 - - [18/May/2026 16:12:56] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:12:58,093 INFO 127.0.0.1 - - [18/May/2026 16:12:58] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:12:58,124 INFO 127.0.0.1 - - [18/May/2026 16:12:58] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:12:58,153 INFO 127.0.0.1 - - [18/May/2026 16:12:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:12:58,168 INFO 127.0.0.1 - - [18/May/2026 16:12:58] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:05,020 INFO 127.0.0.1 - - [18/May/2026 16:13:05] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:13:05,028 INFO 127.0.0.1 - - [18/May/2026 16:13:05] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:13:05,051 INFO 127.0.0.1 - - [18/May/2026 16:13:05] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:05,058 INFO 127.0.0.1 - - [18/May/2026 16:13:05] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:12,547 INFO 127.0.0.1 - - [18/May/2026 16:13:12] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:13:12,579 INFO 127.0.0.1 - - [18/May/2026 16:13:12] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:13:12,610 INFO 127.0.0.1 - - [18/May/2026 16:13:12] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:12,620 INFO 127.0.0.1 - - [18/May/2026 16:13:12] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:20,417 INFO 127.0.0.1 - - [18/May/2026 16:13:20] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:13:20,423 INFO 127.0.0.1 - - [18/May/2026 16:13:20] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:13:20,451 INFO 127.0.0.1 - - [18/May/2026 16:13:20] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:20,455 INFO 127.0.0.1 - - [18/May/2026 16:13:20] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:28,041 INFO 127.0.0.1 - - [18/May/2026 16:13:28] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:13:28,066 INFO 127.0.0.1 - - [18/May/2026 16:13:28] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:30,934 INFO 127.0.0.1 - - [18/May/2026 16:13:30] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:13:30,965 INFO 127.0.0.1 - - [18/May/2026 16:13:30] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:13:30,992 INFO 127.0.0.1 - - [18/May/2026 16:13:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:13:31,005 INFO 127.0.0.1 - - [18/May/2026 16:13:31] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:14:35,046 INFO 127.0.0.1 - - [18/May/2026 16:14:35] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:14:35,062 INFO 127.0.0.1 - - [18/May/2026 16:14:35] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:14:35,087 INFO 127.0.0.1 - - [18/May/2026 16:14:35] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:14:35,101 INFO 127.0.0.1 - - [18/May/2026 16:14:35] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:14:38,952 INFO 127.0.0.1 - - [18/May/2026 16:14:38] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:14:38,982 INFO 127.0.0.1 - - [18/May/2026 16:14:38] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:14:39,014 INFO 127.0.0.1 - - [18/May/2026 16:14:39] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:14:39,027 INFO 127.0.0.1 - - [18/May/2026 16:14:39] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:14:44,356 INFO 127.0.0.1 - - [18/May/2026 16:14:44] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:14:44,369 INFO 127.0.0.1 - - [18/May/2026 16:14:44] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:14:44,402 INFO 127.0.0.1 - - [18/May/2026 16:14:44] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:14:44,406 INFO 127.0.0.1 - - [18/May/2026 16:14:44] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:15:21,266 INFO 127.0.0.1 - - [18/May/2026 16:15:21] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:15:21,306 INFO 127.0.0.1 - - [18/May/2026 16:15:21] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:15:21,338 INFO 127.0.0.1 - - [18/May/2026 16:15:21] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:15:21,349 INFO 127.0.0.1 - - [18/May/2026 16:15:21] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:15:30,146 INFO 127.0.0.1 - - [18/May/2026 16:15:30] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:15:30,154 INFO 127.0.0.1 - - [18/May/2026 16:15:30] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:15:30,182 INFO 127.0.0.1 - - [18/May/2026 16:15:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:15:30,188 INFO 127.0.0.1 - - [18/May/2026 16:15:30] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:16:06,224 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 16:16:06,263 INFO * Restarting with stat
|
||||
2026-05-18 16:16:06,588 WARNING * Debugger is active!
|
||||
2026-05-18 16:16:06,605 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 16:16:37,079 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 16:16:37,115 INFO * Restarting with stat
|
||||
2026-05-18 16:16:37,380 WARNING * Debugger is active!
|
||||
2026-05-18 16:16:37,395 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 16:16:53,487 INFO 127.0.0.1 - - [18/May/2026 16:16:53] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:16:53,538 INFO 127.0.0.1 - - [18/May/2026 16:16:53] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:16:53,578 INFO 127.0.0.1 - - [18/May/2026 16:16:53] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:16:53,585 INFO 127.0.0.1 - - [18/May/2026 16:16:53] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:17:15,730 INFO 127.0.0.1 - - [18/May/2026 16:17:15] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 16:17:15,754 INFO 127.0.0.1 - - [18/May/2026 16:17:15] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:17:17,183 INFO 127.0.0.1 - - [18/May/2026 16:17:17] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:17:17,193 INFO 127.0.0.1 - - [18/May/2026 16:17:17] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:17:17,214 INFO 127.0.0.1 - - [18/May/2026 16:17:17] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:17:17,222 INFO 127.0.0.1 - - [18/May/2026 16:17:17] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:18:55,617 INFO 127.0.0.1 - - [18/May/2026 16:18:55] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:18:55,659 INFO 127.0.0.1 - - [18/May/2026 16:18:55] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:18:55,697 INFO 127.0.0.1 - - [18/May/2026 16:18:55] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:18:55,702 INFO 127.0.0.1 - - [18/May/2026 16:18:55] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:19:07,534 INFO 127.0.0.1 - - [18/May/2026 16:19:07] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:19:07,540 INFO 127.0.0.1 - - [18/May/2026 16:19:07] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:19:07,566 INFO 127.0.0.1 - - [18/May/2026 16:19:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:19:07,573 INFO 127.0.0.1 - - [18/May/2026 16:19:07] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:20:02,281 INFO 127.0.0.1 - - [18/May/2026 16:20:02] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:20:02,318 INFO 127.0.0.1 - - [18/May/2026 16:20:02] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:20:02,349 INFO 127.0.0.1 - - [18/May/2026 16:20:02] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:20:02,364 INFO 127.0.0.1 - - [18/May/2026 16:20:02] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:21:41,997 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 16:21:42,035 INFO * Restarting with stat
|
||||
2026-05-18 16:21:42,351 WARNING * Debugger is active!
|
||||
2026-05-18 16:21:42,371 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 16:22:00,739 INFO 127.0.0.1 - - [18/May/2026 16:22:00] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:22:00,765 INFO 127.0.0.1 - - [18/May/2026 16:22:00] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:22:03,798 INFO 127.0.0.1 - - [18/May/2026 16:22:03] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:22:03,807 INFO 127.0.0.1 - - [18/May/2026 16:22:03] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 16:22:03,831 INFO 127.0.0.1 - - [18/May/2026 16:22:03] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:22:03,837 INFO 127.0.0.1 - - [18/May/2026 16:22:03] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:22:10,489 INFO 127.0.0.1 - - [18/May/2026 16:22:10] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 16:22:10,519 INFO 127.0.0.1 - - [18/May/2026 16:22:10] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:22:10,551 INFO 127.0.0.1 - - [18/May/2026 16:22:10] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:22:10,565 INFO 127.0.0.1 - - [18/May/2026 16:22:10] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:27:08,992 INFO 127.0.0.1 - - [18/May/2026 16:27:08] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:09,026 INFO 127.0.0.1 - - [18/May/2026 16:27:09] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:27:10,123 INFO 127.0.0.1 - - [18/May/2026 16:27:10] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:10,143 INFO 127.0.0.1 - - [18/May/2026 16:27:10] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:27:10,720 INFO 127.0.0.1 - - [18/May/2026 16:27:10] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:10,862 INFO 127.0.0.1 - - [18/May/2026 16:27:10] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:10,880 INFO 127.0.0.1 - - [18/May/2026 16:27:10] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:13,097 INFO 127.0.0.1 - - [18/May/2026 16:27:13] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:13,227 INFO 127.0.0.1 - - [18/May/2026 16:27:13] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:13,246 INFO 127.0.0.1 - - [18/May/2026 16:27:13] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:14,395 INFO 127.0.0.1 - - [18/May/2026 16:27:14] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:14,540 INFO 127.0.0.1 - - [18/May/2026 16:27:14] "GET /static/cancom.svg HTTP/1.1" 200 -
|
||||
2026-05-18 16:27:14,559 INFO 127.0.0.1 - - [18/May/2026 16:27:14] "GET /static/favicon.ico HTTP/1.1" 200 -
|
||||
2026-05-18 16:28:53,584 INFO 127.0.0.1 - - [18/May/2026 16:28:53] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:28:53,607 INFO 127.0.0.1 - - [18/May/2026 16:28:53] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:32:27,829 INFO 127.0.0.1 - - [18/May/2026 16:32:27] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:32:27,852 INFO 127.0.0.1 - - [18/May/2026 16:32:27] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:32:31,315 INFO 127.0.0.1 - - [18/May/2026 16:32:31] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:32:31,344 INFO 127.0.0.1 - - [18/May/2026 16:32:31] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:33:08,100 INFO 127.0.0.1 - - [18/May/2026 16:33:08] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:34:31,532 INFO 127.0.0.1 - - [18/May/2026 16:34:31] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:34:31,555 INFO 127.0.0.1 - - [18/May/2026 16:34:31] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:35:58,216 INFO 127.0.0.1 - - [18/May/2026 16:35:58] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:35:58,239 INFO 127.0.0.1 - - [18/May/2026 16:35:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:36:07,630 INFO 127.0.0.1 - - [18/May/2026 16:36:07] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:36:07,658 INFO 127.0.0.1 - - [18/May/2026 16:36:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:36:09,450 INFO 127.0.0.1 - - [18/May/2026 16:36:09] "GET /transactions/3/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:36:09,478 INFO 127.0.0.1 - - [18/May/2026 16:36:09] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:38:45,796 INFO 127.0.0.1 - - [18/May/2026 16:38:45] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:38:45,826 INFO 127.0.0.1 - - [18/May/2026 16:38:45] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:38:47,255 INFO 127.0.0.1 - - [18/May/2026 16:38:47] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:38:47,284 INFO 127.0.0.1 - - [18/May/2026 16:38:47] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:40:28,208 INFO 127.0.0.1 - - [18/May/2026 16:40:28] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:40:28,239 INFO 127.0.0.1 - - [18/May/2026 16:40:28] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:40:29,482 INFO 127.0.0.1 - - [18/May/2026 16:40:29] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:40:29,515 INFO 127.0.0.1 - - [18/May/2026 16:40:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:41:35,309 INFO 127.0.0.1 - - [18/May/2026 16:41:35] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:41:35,340 INFO 127.0.0.1 - - [18/May/2026 16:41:35] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:41:36,532 INFO 127.0.0.1 - - [18/May/2026 16:41:36] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:41:36,563 INFO 127.0.0.1 - - [18/May/2026 16:41:36] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:43:35,307 INFO 127.0.0.1 - - [18/May/2026 16:43:35] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 16:43:35,339 INFO 127.0.0.1 - - [18/May/2026 16:43:35] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 16:43:38,425 INFO 127.0.0.1 - - [18/May/2026 16:43:38] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 16:43:38,450 INFO 127.0.0.1 - - [18/May/2026 16:43:38] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:39:04,523 INFO 127.0.0.1 - - [18/May/2026 19:39:04] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:39:04,628 INFO 127.0.0.1 - - [18/May/2026 19:39:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:39:07,709 INFO 127.0.0.1 - - [18/May/2026 19:39:07] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:39:07,740 INFO 127.0.0.1 - - [18/May/2026 19:39:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:41:24,459 INFO 127.0.0.1 - - [18/May/2026 19:41:24] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:41:24,488 INFO 127.0.0.1 - - [18/May/2026 19:41:24] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:41:27,973 INFO 127.0.0.1 - - [18/May/2026 19:41:27] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:41:28,005 INFO 127.0.0.1 - - [18/May/2026 19:41:28] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:43:07,610 INFO 127.0.0.1 - - [18/May/2026 19:43:07] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:43:07,638 INFO 127.0.0.1 - - [18/May/2026 19:43:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:43:10,083 INFO 127.0.0.1 - - [18/May/2026 19:43:10] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:43:10,105 INFO 127.0.0.1 - - [18/May/2026 19:43:10] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:45:21,476 INFO 127.0.0.1 - - [18/May/2026 19:45:21] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:45:21,505 INFO 127.0.0.1 - - [18/May/2026 19:45:21] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:45:24,274 INFO 127.0.0.1 - - [18/May/2026 19:45:24] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:45:24,303 INFO 127.0.0.1 - - [18/May/2026 19:45:24] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:45:27,394 INFO 127.0.0.1 - - [18/May/2026 19:45:27] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:45:27,417 INFO 127.0.0.1 - - [18/May/2026 19:45:27] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:45:29,529 INFO 127.0.0.1 - - [18/May/2026 19:45:29] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:45:29,555 INFO 127.0.0.1 - - [18/May/2026 19:45:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:47:04,445 INFO 127.0.0.1 - - [18/May/2026 19:47:04] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:47:04,472 INFO 127.0.0.1 - - [18/May/2026 19:47:04] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:47:06,739 INFO 127.0.0.1 - - [18/May/2026 19:47:06] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:47:06,769 INFO 127.0.0.1 - - [18/May/2026 19:47:06] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:48:07,688 INFO 127.0.0.1 - - [18/May/2026 19:48:07] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:48:07,713 INFO 127.0.0.1 - - [18/May/2026 19:48:07] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:48:09,135 INFO 127.0.0.1 - - [18/May/2026 19:48:09] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:48:09,155 INFO 127.0.0.1 - - [18/May/2026 19:48:09] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:49:17,078 INFO 127.0.0.1 - - [18/May/2026 19:49:17] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:49:39,165 INFO 127.0.0.1 - - [18/May/2026 19:49:39] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:49:44,653 INFO 127.0.0.1 - - [18/May/2026 19:49:44] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:49:44,684 INFO 127.0.0.1 - - [18/May/2026 19:49:44] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:49:46,033 INFO 127.0.0.1 - - [18/May/2026 19:49:46] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:49:46,056 INFO 127.0.0.1 - - [18/May/2026 19:49:46] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:52:53,735 INFO 127.0.0.1 - - [18/May/2026 19:52:53] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:52:53,763 INFO 127.0.0.1 - - [18/May/2026 19:52:53] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:52:55,021 INFO 127.0.0.1 - - [18/May/2026 19:52:55] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 19:52:55,051 INFO 127.0.0.1 - - [18/May/2026 19:52:55] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:53:13,216 INFO 127.0.0.1 - - [18/May/2026 19:53:13] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:53:13,246 INFO 127.0.0.1 - - [18/May/2026 19:53:13] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:55:55,995 INFO 127.0.0.1 - - [18/May/2026 19:55:55] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:55:56,018 INFO 127.0.0.1 - - [18/May/2026 19:55:56] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:58:45,997 INFO 127.0.0.1 - - [18/May/2026 19:58:45] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:58:46,020 INFO 127.0.0.1 - - [18/May/2026 19:58:46] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 19:59:36,278 INFO 127.0.0.1 - - [18/May/2026 19:59:36] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 19:59:36,301 INFO 127.0.0.1 - - [18/May/2026 19:59:36] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:34:14,764 INFO 127.0.0.1 - - [18/May/2026 20:34:14] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:34:14,806 INFO 127.0.0.1 - - [18/May/2026 20:34:14] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:34:23,677 INFO [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||
* Running on http://127.0.0.1:5000
|
||||
2026-05-18 20:34:23,677 INFO [33mPress CTRL+C to quit[0m
|
||||
2026-05-18 20:34:23,681 INFO * Restarting with stat
|
||||
2026-05-18 20:34:23,902 WARNING * Debugger is active!
|
||||
2026-05-18 20:34:23,920 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 20:38:11,471 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 20:38:11,511 INFO * Restarting with stat
|
||||
2026-05-18 20:38:11,838 WARNING * Debugger is active!
|
||||
2026-05-18 20:38:11,866 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 20:40:33,937 INFO 127.0.0.1 - - [18/May/2026 20:40:33] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:40:33,994 INFO 127.0.0.1 - - [18/May/2026 20:40:33] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:40:41,541 INFO 127.0.0.1 - - [18/May/2026 20:40:41] "GET /admin/import HTTP/1.1" 200 -
|
||||
2026-05-18 20:40:41,564 INFO 127.0.0.1 - - [18/May/2026 20:40:41] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:43:25,032 INFO 127.0.0.1 - - [18/May/2026 20:43:25] "[32mPOST /admin/import HTTP/1.1[0m" 302 -
|
||||
2026-05-18 20:43:25,041 INFO 127.0.0.1 - - [18/May/2026 20:43:25] "GET /admin/import HTTP/1.1" 200 -
|
||||
2026-05-18 20:43:25,075 INFO 127.0.0.1 - - [18/May/2026 20:43:25] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:43:25,087 INFO 127.0.0.1 - - [18/May/2026 20:43:25] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:44:03,384 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 20:44:03,422 INFO * Restarting with stat
|
||||
2026-05-18 20:44:03,768 WARNING * Debugger is active!
|
||||
2026-05-18 20:44:03,787 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 20:45:25,715 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 20:45:25,753 INFO * Restarting with stat
|
||||
2026-05-18 20:45:27,192 WARNING * Debugger is active!
|
||||
2026-05-18 20:45:27,225 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 20:46:16,616 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 20:46:16,648 INFO * Restarting with stat
|
||||
2026-05-18 20:46:17,008 WARNING * Debugger is active!
|
||||
2026-05-18 20:46:17,025 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 20:46:54,852 INFO * Detected change in '/Users/erik/Documents/DEV/Key Verwaltung/app.py', reloading
|
||||
2026-05-18 20:46:54,888 INFO * Restarting with stat
|
||||
2026-05-18 20:46:55,230 WARNING * Debugger is active!
|
||||
2026-05-18 20:46:55,252 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 20:47:20,700 INFO 127.0.0.1 - - [18/May/2026 20:47:20] "GET /admin/import HTTP/1.1" 200 -
|
||||
2026-05-18 20:47:20,732 INFO 127.0.0.1 - - [18/May/2026 20:47:20] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:47:40,793 INFO 127.0.0.1 - - [18/May/2026 20:47:40] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 20:47:40,818 INFO 127.0.0.1 - - [18/May/2026 20:47:40] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:47:46,284 INFO 127.0.0.1 - - [18/May/2026 20:47:46] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 20:47:46,306 INFO 127.0.0.1 - - [18/May/2026 20:47:46] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:47:47,796 INFO 127.0.0.1 - - [18/May/2026 20:47:47] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 20:47:47,819 INFO 127.0.0.1 - - [18/May/2026 20:47:47] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:47:50,031 INFO 127.0.0.1 - - [18/May/2026 20:47:50] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 20:47:50,055 INFO 127.0.0.1 - - [18/May/2026 20:47:50] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:00,202 INFO 127.0.0.1 - - [18/May/2026 20:48:00] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:00,233 INFO 127.0.0.1 - - [18/May/2026 20:48:00] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:23,087 INFO 127.0.0.1 - - [18/May/2026 20:48:23] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:23,112 INFO 127.0.0.1 - - [18/May/2026 20:48:23] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:26,546 INFO 127.0.0.1 - - [18/May/2026 20:48:26] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:26,566 INFO 127.0.0.1 - - [18/May/2026 20:48:26] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:27,384 INFO 127.0.0.1 - - [18/May/2026 20:48:27] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:27,404 INFO 127.0.0.1 - - [18/May/2026 20:48:27] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:28,020 INFO 127.0.0.1 - - [18/May/2026 20:48:28] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:28,041 INFO 127.0.0.1 - - [18/May/2026 20:48:28] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:28,663 INFO 127.0.0.1 - - [18/May/2026 20:48:28] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:28,681 INFO 127.0.0.1 - - [18/May/2026 20:48:28] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:29,491 INFO 127.0.0.1 - - [18/May/2026 20:48:29] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:29,512 INFO 127.0.0.1 - - [18/May/2026 20:48:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:43,006 INFO 127.0.0.1 - - [18/May/2026 20:48:43] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:43,028 INFO 127.0.0.1 - - [18/May/2026 20:48:43] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:43,800 INFO 127.0.0.1 - - [18/May/2026 20:48:43] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:43,821 INFO 127.0.0.1 - - [18/May/2026 20:48:43] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:44,531 INFO 127.0.0.1 - - [18/May/2026 20:48:44] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:44,548 INFO 127.0.0.1 - - [18/May/2026 20:48:44] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:45,340 INFO 127.0.0.1 - - [18/May/2026 20:48:45] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:45,362 INFO 127.0.0.1 - - [18/May/2026 20:48:45] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:46,156 INFO 127.0.0.1 - - [18/May/2026 20:48:46] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:46,175 INFO 127.0.0.1 - - [18/May/2026 20:48:46] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:47,169 INFO 127.0.0.1 - - [18/May/2026 20:48:47] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:47,189 INFO 127.0.0.1 - - [18/May/2026 20:48:47] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:47,910 INFO 127.0.0.1 - - [18/May/2026 20:48:47] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:47,929 INFO 127.0.0.1 - - [18/May/2026 20:48:47] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:48,678 INFO 127.0.0.1 - - [18/May/2026 20:48:48] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:48,697 INFO 127.0.0.1 - - [18/May/2026 20:48:48] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:56,021 INFO 127.0.0.1 - - [18/May/2026 20:48:56] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:56,039 INFO 127.0.0.1 - - [18/May/2026 20:48:56] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:59,558 INFO 127.0.0.1 - - [18/May/2026 20:48:59] "[32mGET /logout HTTP/1.1[0m" 302 -
|
||||
2026-05-18 20:48:59,567 INFO 127.0.0.1 - - [18/May/2026 20:48:59] "GET /login HTTP/1.1" 200 -
|
||||
2026-05-18 20:48:59,587 INFO 127.0.0.1 - - [18/May/2026 20:48:59] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:48:59,600 INFO 127.0.0.1 - - [18/May/2026 20:48:59] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:49:09,950 INFO 127.0.0.1 - - [18/May/2026 20:49:09] "[32mPOST /login HTTP/1.1[0m" 302 -
|
||||
2026-05-18 20:49:09,983 INFO 127.0.0.1 - - [18/May/2026 20:49:09] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 20:49:10,015 INFO 127.0.0.1 - - [18/May/2026 20:49:10] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:49:10,030 INFO 127.0.0.1 - - [18/May/2026 20:49:10] "[36mGET /static/favicon.ico HTTP/1.1[0m" 304 -
|
||||
2026-05-18 20:56:44,123 INFO [31m[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.[0m
|
||||
* Running on http://127.0.0.1:5000
|
||||
2026-05-18 20:56:44,125 INFO [33mPress CTRL+C to quit[0m
|
||||
2026-05-18 20:56:44,126 INFO * Restarting with stat
|
||||
2026-05-18 20:56:44,370 WARNING * Debugger is active!
|
||||
2026-05-18 20:56:44,388 INFO * Debugger PIN: 428-899-358
|
||||
2026-05-18 21:00:29,403 INFO 127.0.0.1 - - [18/May/2026 21:00:29] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:00:29,431 INFO 127.0.0.1 - - [18/May/2026 21:00:29] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:00:30,119 INFO 127.0.0.1 - - [18/May/2026 21:00:30] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:00:30,140 INFO 127.0.0.1 - - [18/May/2026 21:00:30] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:00:46,795 INFO 127.0.0.1 - - [18/May/2026 21:00:46] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 21:00:46,818 INFO 127.0.0.1 - - [18/May/2026 21:00:46] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:00:48,123 INFO 127.0.0.1 - - [18/May/2026 21:00:48] "GET /assign HTTP/1.1" 200 -
|
||||
2026-05-18 21:00:48,145 INFO 127.0.0.1 - - [18/May/2026 21:00:48] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:00:49,862 INFO 127.0.0.1 - - [18/May/2026 21:00:49] "GET /return HTTP/1.1" 200 -
|
||||
2026-05-18 21:00:49,893 INFO 127.0.0.1 - - [18/May/2026 21:00:49] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:00:55,336 INFO 127.0.0.1 - - [18/May/2026 21:00:55] "GET /admin/import HTTP/1.1" 200 -
|
||||
2026-05-18 21:00:55,364 INFO 127.0.0.1 - - [18/May/2026 21:00:55] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:00:58,118 INFO 127.0.0.1 - - [18/May/2026 21:00:58] "GET /admin/staff HTTP/1.1" 200 -
|
||||
2026-05-18 21:00:58,144 INFO 127.0.0.1 - - [18/May/2026 21:00:58] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:01:00,607 INFO 127.0.0.1 - - [18/May/2026 21:01:00] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 21:01:00,631 INFO 127.0.0.1 - - [18/May/2026 21:01:00] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:01:02,294 INFO 127.0.0.1 - - [18/May/2026 21:01:02] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:01:02,315 INFO 127.0.0.1 - - [18/May/2026 21:01:02] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:01:09,803 INFO 127.0.0.1 - - [18/May/2026 21:01:09] "GET /transactions/4/print HTTP/1.1" 200 -
|
||||
2026-05-18 21:01:09,828 INFO 127.0.0.1 - - [18/May/2026 21:01:09] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:01:47,672 INFO 127.0.0.1 - - [18/May/2026 21:01:47] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:01:47,698 INFO 127.0.0.1 - - [18/May/2026 21:01:47] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:03:19,621 INFO 127.0.0.1 - - [18/May/2026 21:03:19] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:03:19,647 INFO 127.0.0.1 - - [18/May/2026 21:03:19] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:04:59,841 INFO 127.0.0.1 - - [18/May/2026 21:04:59] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:04:59,864 INFO 127.0.0.1 - - [18/May/2026 21:04:59] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:07:00,533 INFO 127.0.0.1 - - [18/May/2026 21:07:00] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:07:00,556 INFO 127.0.0.1 - - [18/May/2026 21:07:00] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:07:14,520 INFO 127.0.0.1 - - [18/May/2026 21:07:14] "GET /users/new HTTP/1.1" 200 -
|
||||
2026-05-18 21:07:14,547 INFO 127.0.0.1 - - [18/May/2026 21:07:14] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:08:35,615 INFO 127.0.0.1 - - [18/May/2026 21:08:35] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:08:35,653 INFO 127.0.0.1 - - [18/May/2026 21:08:35] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:09:44,487 INFO 127.0.0.1 - - [18/May/2026 21:09:44] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:09:44,511 INFO 127.0.0.1 - - [18/May/2026 21:09:44] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:09:46,147 INFO 127.0.0.1 - - [18/May/2026 21:09:46] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:09:46,169 INFO 127.0.0.1 - - [18/May/2026 21:09:46] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:10:10,350 INFO 127.0.0.1 - - [18/May/2026 21:10:10] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:10:10,369 INFO 127.0.0.1 - - [18/May/2026 21:10:10] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:11:01,721 INFO 127.0.0.1 - - [18/May/2026 21:11:01] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:11:01,744 INFO 127.0.0.1 - - [18/May/2026 21:11:01] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
2026-05-18 21:12:05,298 INFO 127.0.0.1 - - [18/May/2026 21:12:05] "GET / HTTP/1.1" 200 -
|
||||
2026-05-18 21:12:05,322 INFO 127.0.0.1 - - [18/May/2026 21:12:05] "[36mGET /static/cancom.svg HTTP/1.1[0m" 304 -
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
Flask==3.0.3
|
||||
4408
session-ses_1c3a.md
Normal file
4408
session-ses_1c3a.md
Normal file
File diff suppressed because one or more lines are too long
4
static/cancom.svg
Executable file
4
static/cancom.svg
Executable file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Created with xTool Creative Space (https://www.xtool.com/pages/software) -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xcs="https://www.xtool.com/pages/software" version="1.1" preserveAspectRatio="xMinYMin meet" width="61.682394106284505mm" height="9.909010347638684mm" viewBox="136.55880294685778 209.2954948261807 61.682394106284505 9.909010347638684" xcs:version="2.7.22"><style></style>
|
||||
<path transform="matrix(0.158038,0,0,0.158038,133.2558,163.322122)" stroke="none" fill="#ffffff" data-view-type="laser" d="M125.9,352.1h18.9L119,291.7h-16.2l-25,60.4h18.2l14.6-38.9L125.9,352.1z M323.6,322 c0,8.8-6.1,16.4-15.4,16.4c-9.1,0-15.4-7.6-15.4-16.4s6.1-16.4,15.2-16.4C317,305.6,323.6,313.4,323.6,322 M340.5,322.3 c0-17.4-13.4-31.3-32.3-31.3c-18.7,0-32.3,14.1-32.3,31.3c0,17.4,13.4,31.3,32.3,31.3C326.9,353.6,340.5,339.4,340.5,322.3 M230.4,322.5c0-8.8,6.1-16.4,14.9-16.4c3,0,5.8,0.8,8.1,2l6.6-13.4c-3-2-8.1-3.8-14.9-3.8c-18.2,0-31.6,14.1-31.6,31.3 c0,18.2,12.6,31.1,31.6,31.1c17.2,0,26-10.6,28.8-21l-13.6-5.6c-1.8,5.8-6.3,11.6-14.4,11.6C236.5,338.4,230.4,331.4,230.4,322.5 M38,322.5c0-8.8,6.1-16.4,14.9-16.4c3,0,5.8,0.8,8.1,2l6.6-13.4c-3-2-8.1-3.8-14.9-3.8c-18.4,0-31.8,14.1-31.8,31.3 c0,18.2,12.6,31.1,31.6,31.1c17.2,0,26-10.6,28.8-21l-13.6-5.6c-1.8,5.8-6.3,11.6-14.4,11.6C44,338.4,38,331.4,38,322.5 M191.8,352.1h14.4v-59.8H190v33.1l-25-33.1h-15.4v60.1h16.2v-34.1L191.8,352.1z M411.2,352.1v-59.8h-17.7l-14.4,23.2l-14.4-23.2 h-17.7v60.1h16.4V318l15.4,23.2h0.3l15.4-23.5v34.6L411.2,352.1L411.2,352.1z" fill-rule="nonzero"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
static/favicon.ico
Executable file
BIN
static/favicon.ico
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
37
templates/assign_asset.html
Normal file
37
templates/assign_asset.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-section-title">
|
||||
<h1>Ausgabe</h1>
|
||||
<p>Tuerchips und Parkkarten an bestehende User ausgeben.</p>
|
||||
</div>
|
||||
|
||||
<section class="card form-card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title"><i class="ti ti-key me-1"></i>Chip oder Parkkarte ausgeben</h2>
|
||||
<form method="post" action="{{ url_for('assign_asset') }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">User</label>
|
||||
<select class="form-select" name="user_id" required>
|
||||
<option value="">Bitte waehlen</option>
|
||||
{% for user in users %}
|
||||
<option value="{{ user.id }}">{{ user.full_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Typ</label>
|
||||
<select class="form-select" name="asset_type" required>
|
||||
<option value="chip">Tuerchip</option>
|
||||
<option value="parking_card">Parkkarte</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kennung</label>
|
||||
<input class="form-control" type="text" name="asset_code" required placeholder="z. B. CHIP-1001">
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Ausgabe speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
561
templates/base.html
Normal file
561
templates/base.html
Normal file
@@ -0,0 +1,561 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ title or "Chipverwaltung" }}</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
<link rel="stylesheet" href="https://unpkg.com/@tabler/core@1.0.0-beta20/dist/css/tabler.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--ccm-bg: #f4f6f8;
|
||||
--ccm-text: #212121;
|
||||
--ccm-primary: #da002d;
|
||||
--ccm-primary-hover: #b00024;
|
||||
--ccm-header: #2b2f36;
|
||||
--ccm-surface: #ffffff;
|
||||
--ccm-border: #d9dee3;
|
||||
--ccm-muted: #6b7280;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--ccm-bg);
|
||||
color: var(--ccm-text);
|
||||
font-family: "Segoe UI", Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
.card-title,
|
||||
.navbar-brand-wordmark strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.navbar-brand-wordmark {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.1;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.navbar-brand-logo {
|
||||
height: 1.85rem;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.brand-surface {
|
||||
background-color: var(--ccm-header);
|
||||
margin-bottom: 0;
|
||||
border-bottom: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.brand-surface .navbar-brand-wordmark,
|
||||
.brand-surface .nav-link,
|
||||
.brand-surface .navbar-nav .btn {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.brand-surface .btn-outline-secondary {
|
||||
border-color: rgba(255, 255, 255, 0.35);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.brand-surface .btn-outline-secondary:hover,
|
||||
.brand-surface .btn-outline-secondary:focus {
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.navbar-brand-wordmark strong {
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.app-shell {
|
||||
width: 100%;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1.5rem 2.5rem;
|
||||
}
|
||||
|
||||
.auth-shell {
|
||||
min-height: calc(100vh - 72px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.page-shell {
|
||||
width: 100%;
|
||||
max-width: 1320px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.card.stat-card {
|
||||
border: 0;
|
||||
color: #ffffff;
|
||||
border-radius: 1rem;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 18px 34px rgba(43, 47, 54, 0.14);
|
||||
}
|
||||
|
||||
.card.stat-card .card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.card.stat-card .stat-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.card.stat-card .stat-value {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.card.stat-card .stat-icon {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card.stat-card.stat-card-users {
|
||||
background: linear-gradient(135deg, #da002d 0%, #b00024 100%);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.card.stat-card.stat-card-chips {
|
||||
background: linear-gradient(135deg, #2b2f36 0%, #434a54 100%);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.card.stat-card.stat-card-cards {
|
||||
background: linear-gradient(135deg, #8c0f2b 0%, #da002d 100%);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--ccm-primary);
|
||||
border-color: var(--ccm-primary);
|
||||
}
|
||||
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: var(--ccm-primary-hover);
|
||||
border-color: var(--ccm-primary-hover);
|
||||
}
|
||||
|
||||
.nav-surface {
|
||||
background-color: var(--ccm-primary);
|
||||
margin-top: 0;
|
||||
border-top: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.navbar-expand-md,
|
||||
.navbar-collapse,
|
||||
.navbar-collapse .navbar {
|
||||
margin-top: 0;
|
||||
border-top: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
header.navbar,
|
||||
.navbar-dark.nav-surface,
|
||||
.collapse.navbar-collapse.show {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.nav-surface .btn {
|
||||
min-width: 132px;
|
||||
}
|
||||
|
||||
.nav-surface .btn-white {
|
||||
color: var(--ccm-primary);
|
||||
border-color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.nav-surface .btn-white:hover,
|
||||
.nav-surface .btn-white:focus {
|
||||
background-color: #e9ecef;
|
||||
border-color: #e9ecef;
|
||||
color: var(--ccm-primary-hover);
|
||||
}
|
||||
|
||||
.nav-surface .btn.active {
|
||||
background-color: #ffffff;
|
||||
border-color: #ffffff;
|
||||
color: var(--ccm-primary-hover);
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.flash-stack {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
position: fixed;
|
||||
top: 8.5rem;
|
||||
right: 1rem;
|
||||
width: min(420px, calc(100vw - 2rem));
|
||||
z-index: 1080;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.flash-stack {
|
||||
top: 7.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.card-table .btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--ccm-muted);
|
||||
}
|
||||
|
||||
.top-actions a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn .ti,
|
||||
.nav-surface .ti {
|
||||
font-size: 1rem;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.content-stack {
|
||||
width: 100%;
|
||||
max-width: 1320px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.content-stack-wide {
|
||||
width: 100%;
|
||||
max-width: 1360px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid var(--ccm-border);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 12px 30px rgba(43, 47, 54, 0.06);
|
||||
background: var(--ccm-surface);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: transparent;
|
||||
border-bottom: 1px solid var(--ccm-border);
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
color: var(--ccm-text);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
color: var(--ccm-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-select {
|
||||
border-color: var(--ccm-border);
|
||||
min-height: 2.75rem;
|
||||
}
|
||||
|
||||
.form-control:focus,
|
||||
.form-select:focus {
|
||||
border-color: rgba(218, 0, 45, 0.45);
|
||||
box-shadow: 0 0 0 0.2rem rgba(218, 0, 45, 0.12);
|
||||
}
|
||||
|
||||
.table {
|
||||
--tblr-table-striped-bg: #fafbfc;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
color: var(--ccm-muted);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.03em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
color: #5d0016;
|
||||
background: rgba(218, 0, 45, 0.08);
|
||||
border: 1px solid rgba(218, 0, 45, 0.16);
|
||||
box-shadow: 0 12px 28px rgba(43, 47, 54, 0.14);
|
||||
animation: fadeout 0.35s ease forwards;
|
||||
animation-delay: 3.5s;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
color: #0f5132;
|
||||
background: #d1e7dd;
|
||||
border: 1px solid #badbcc;
|
||||
box-shadow: 0 12px 28px rgba(43, 47, 54, 0.14);
|
||||
animation: fadeout 0.35s ease forwards;
|
||||
animation-delay: 3.5s;
|
||||
}
|
||||
|
||||
.alert-error,
|
||||
.alert-danger {
|
||||
color: #842029;
|
||||
background: #f8d7da;
|
||||
border: 1px solid #f5c2c7;
|
||||
box-shadow: 0 12px 28px rgba(43, 47, 54, 0.14);
|
||||
}
|
||||
|
||||
.alert-import-errors {
|
||||
color: #842029;
|
||||
background: #fff5f5;
|
||||
border: 1px solid #f1b0b7;
|
||||
box-shadow: 0 14px 30px rgba(43, 47, 54, 0.16);
|
||||
max-height: min(70vh, 36rem);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.flash-list {
|
||||
margin: 0.75rem 0 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.flash-list li + li {
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
@keyframes fadeout {
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-dismissible .btn-close {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.page-section-title {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.page-section-title h1 {
|
||||
margin: 0;
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.page-section-title p {
|
||||
margin: 0.35rem 0 0;
|
||||
color: var(--ccm-muted);
|
||||
}
|
||||
|
||||
.app-footer {
|
||||
margin-top: 1rem;
|
||||
padding: 0.65rem 1.5rem 0.95rem;
|
||||
color: var(--ccm-muted);
|
||||
font-size: 0.82rem;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.app-footer-inner {
|
||||
width: 100%;
|
||||
max-width: 1320px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.2rem 0.75rem;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.app-footer-separator {
|
||||
color: #b0b7c3;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.card-header,
|
||||
.card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.table thead th,
|
||||
.table tbody td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-surface .btn {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="flash-stack">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }} alert-dismissible" role="alert">
|
||||
{% if category == 'import-errors' %}
|
||||
<strong>Import konnte nicht ausgefuehrt werden.</strong>
|
||||
<ul class="flash-list mb-0">
|
||||
{% for entry in message %}
|
||||
<li>{{ entry }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ message }}
|
||||
{% endif %}
|
||||
<button type="button" class="btn-close" aria-label="Schliessen" onclick="this.parentElement.remove();"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<header class="navbar navbar-expand-md d-print-none brand-surface">
|
||||
<div class="container-xl">
|
||||
<div class="navbar-brand navbar-brand-autodark pe-0 pe-md-3">
|
||||
<img class="navbar-brand-logo me-3" src="{{ url_for('static', filename='cancom.svg') }}" alt="CANCOM Logo">
|
||||
<span class="navbar-brand-wordmark">
|
||||
<strong>CANCOM BU Sued/West</strong>
|
||||
<span>Tuerchip und Parkkartenverwaltung</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="navbar-nav flex-row order-md-last top-actions">
|
||||
{% if g.current_staff %}
|
||||
<span class="nav-link disabled">{{ g.current_staff.full_name }} ({{ g.current_staff.role }})</span>
|
||||
<a class="btn btn-outline-secondary" href="{{ url_for('logout') }}"><i class="ti ti-logout me-1"></i>Abmelden</a>
|
||||
{% else %}
|
||||
<a class="btn btn-primary" href="{{ url_for('login') }}"><i class="ti ti-login me-1"></i>Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{% if g.current_staff %}
|
||||
<div class="navbar-expand-md">
|
||||
<div class="collapse navbar-collapse show">
|
||||
<div class="navbar navbar-dark nav-surface">
|
||||
<div class="container-xl">
|
||||
<div class="d-flex flex-wrap gap-2 py-2">
|
||||
<a class="btn btn-white {% if request.endpoint == 'index' %}active{% endif %}" href="{{ url_for('index') }}"><i class="ti ti-layout-dashboard me-1"></i>Uebersicht</a>
|
||||
<a class="btn btn-white {% if request.endpoint == 'create_user' %}active{% endif %}" href="{{ url_for('create_user') }}"><i class="ti ti-user-plus me-1"></i>User anlegen</a>
|
||||
<a class="btn btn-white {% if request.endpoint == 'assign_asset' %}active{% endif %}" href="{{ url_for('assign_asset') }}"><i class="ti ti-key me-1"></i>Ausgabe</a>
|
||||
<a class="btn btn-white {% if request.endpoint == 'return_asset' %}active{% endif %}" href="{{ url_for('return_asset') }}"><i class="ti ti-arrow-back-up me-1"></i>Rueckgabe</a>
|
||||
{% if g.current_staff.role == 'admin' %}
|
||||
<a class="btn btn-white {% if request.endpoint == 'import_data' %}active{% endif %}" href="{{ url_for('import_data') }}"><i class="ti ti-file-import me-1"></i>Import</a>
|
||||
<a class="btn btn-white {% if request.endpoint == 'manage_staff' %}active{% endif %}" href="{{ url_for('manage_staff') }}"><i class="ti ti-users-group me-1"></i>Bearbeiterverwaltung</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="page-wrapper">
|
||||
<div class="{% if request.endpoint == 'login' %}auth-shell{% else %}app-shell{% endif %}">
|
||||
<div class="{% if request.endpoint == 'login' %}login-card{% else %}page-shell{% endif %}">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="app-footer">
|
||||
<div class="app-footer-inner">
|
||||
<span>Autor Erik Thiele</span>
|
||||
<span class="app-footer-separator">|</span>
|
||||
<span>© {{ app_year }} CANCOM GmbH</span>
|
||||
<span class="app-footer-separator">|</span>
|
||||
<span>Version {{ app_version }}</span>
|
||||
<span class="app-footer-separator">|</span>
|
||||
<span>Host {{ app_host }}</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<script>
|
||||
window.addEventListener("load", () => {
|
||||
window.setTimeout(() => {
|
||||
document.querySelectorAll(".flash-stack .alert").forEach((element) => {
|
||||
if (element.classList.contains("alert-error") || element.classList.contains("alert-danger") || element.classList.contains("alert-import-errors")) {
|
||||
return;
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
element.remove();
|
||||
}, 500);
|
||||
});
|
||||
}, 4000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
29
templates/create_user.html
Normal file
29
templates/create_user.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-section-title">
|
||||
<h1>User anlegen</h1>
|
||||
<p>Neue Anwender fuer die Verwaltung von Tuerchips und Parkkarten erfassen.</p>
|
||||
</div>
|
||||
|
||||
<section class="card form-card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title"><i class="ti ti-user-plus me-1"></i>Neuen User anlegen</h2>
|
||||
<form method="post" action="{{ url_for('create_user') }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Vollstaendiger Name</label>
|
||||
<input class="form-control" type="text" name="full_name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">E-Mail</label>
|
||||
<input class="form-control" type="email" name="email">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Abteilung</label>
|
||||
<input class="form-control" type="text" name="department">
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit"><i class="ti ti-device-floppy me-1"></i>User speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
27
templates/import_data.html
Normal file
27
templates/import_data.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-section-title">
|
||||
<h1>Import</h1>
|
||||
<p>Vorhandene Bestandsdaten zeilenweise in die Datenbank uebernehmen.</p>
|
||||
</div>
|
||||
|
||||
<section class="card form-card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title"><i class="ti ti-file-import me-1"></i>Importdaten einfuegen</h2>
|
||||
<p class="muted">CSV-Format pro Zeile: <code>User;Typ;Kennung;Aktion</code>. Fuer die Aktion ist <code>Import</code> vorgesehen.</p>
|
||||
<form method="post" action="{{ url_for('import_data') }}" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">CSV-Datei</label>
|
||||
<input class="form-control" type="file" name="import_file" accept=".csv,text/csv">
|
||||
<div class="form-hint">UTF-8, Semikolon-getrennt. Wenn eine Datei ausgewaehlt ist, wird sie bevorzugt importiert.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Importzeilen alternativ direkt einfuegen</label>
|
||||
<textarea class="form-control" name="import_rows" rows="12" placeholder="Max Mustermann;Tuerchip;CHIP-1001;Import Erika Muster;Parkkarte;PARK-2001;Import"></textarea>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit"><i class="ti ti-database-import me-1"></i>Import ausfuehren</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
172
templates/index.html
Normal file
172
templates/index.html
Normal file
@@ -0,0 +1,172 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-section-title">
|
||||
<h1>Uebersicht</h1>
|
||||
<p>Zentraler Einstieg fuer Bestand, Suche, Bewegungen und Protokolle.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid mb-4">
|
||||
<section class="card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title"><i class="ti ti-search me-1"></i>Suche</h2>
|
||||
<form method="get" action="{{ url_for('index') }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Name, E-Mail oder Kennung</label>
|
||||
<input class="form-control" type="text" name="q" value="{{ search_query }}" placeholder="z. B. Max oder CHIP-1001">
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">Suchen</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title"><i class="ti ti-chart-donut-3 me-1"></i>Bestand</h2>
|
||||
<div class="stats-grid">
|
||||
<div class="card stat-card stat-card-users">
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="stat-label">User</div>
|
||||
<div class="stat-value">{{ stats.users }}</div>
|
||||
</div>
|
||||
<div class="stat-icon"><i class="ti ti-users"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card stat-card stat-card-chips">
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="stat-label">Tuerchips</div>
|
||||
<div class="stat-value">{{ stats.active_chips }}</div>
|
||||
</div>
|
||||
<div class="stat-icon"><i class="ti ti-key"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card stat-card stat-card-cards">
|
||||
<div class="card-body">
|
||||
<div>
|
||||
<div class="stat-label">Parkkarten</div>
|
||||
<div class="stat-value">{{ stats.active_cards }}</div>
|
||||
</div>
|
||||
<div class="stat-icon"><i class="ti ti-credit-card"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title"><i class="ti ti-users me-1"></i>User</h2>
|
||||
</div>
|
||||
<div class="table-responsive table-wrap">
|
||||
{% if users %}
|
||||
<table class="table table-vcenter card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>E-Mail</th>
|
||||
<th>Abteilung</th>
|
||||
<th>Tuerchip</th>
|
||||
<th>Parkkarte</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>{{ user.full_name }}</td>
|
||||
<td>{{ user.email or "-" }}</td>
|
||||
<td>{{ user.department or "-" }}</td>
|
||||
<td>
|
||||
{% if user.chip_code %}
|
||||
{{ user.chip_code }}<br>
|
||||
<span class="muted">seit {{ user.chip_assigned_at }}</span>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if user.parking_card_code %}
|
||||
{{ user.parking_card_code }}<br>
|
||||
<span class="muted">seit {{ user.parking_card_assigned_at }}</span>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="card-body"><p class="mb-0">Keine passenden User gefunden.</p></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title"><i class="ti ti-history me-1"></i>Letzte Bewegungen</h2>
|
||||
</div>
|
||||
<div class="table-responsive table-wrap">
|
||||
{% if transactions %}
|
||||
<table class="table table-vcenter card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zeitpunkt</th>
|
||||
<th>User</th>
|
||||
<th>Typ</th>
|
||||
<th>Kennung</th>
|
||||
<th>Bearbeiter</th>
|
||||
<th>Aktion</th>
|
||||
<th>Beleg</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in transactions %}
|
||||
<tr>
|
||||
<td>{{ entry.created_at }}</td>
|
||||
<td>{{ entry.full_name }}</td>
|
||||
<td>{{ asset_labels[entry.asset_type] }}</td>
|
||||
<td>{{ entry.asset_code }}</td>
|
||||
<td>{{ entry.handled_by or "-" }}</td>
|
||||
<td>{{ action_labels[entry.action] }}</td>
|
||||
<td><a class="btn btn-sm btn-outline-secondary" href="{{ url_for('print_transaction', transaction_id=entry.id) }}"><i class="ti ti-printer me-1"></i>Druck</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="card-body"><p class="mb-0">Noch keine Bewegungen erfasst.</p></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% if g.current_staff and g.current_staff.role == 'admin' %}
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title"><i class="ti ti-file-text me-1"></i>Letzte Logeintraege</h2>
|
||||
</div>
|
||||
<div class="table-responsive table-wrap">
|
||||
{% if recent_logs %}
|
||||
<table class="table table-vcenter card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Eintrag</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for line in recent_logs %}
|
||||
<tr>
|
||||
<td><code>{{ line }}</code></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="card-body"><p class="mb-0">Noch keine Logeintraege vorhanden.</p></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
20
templates/login.html
Normal file
20
templates/login.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="card card-md login-card">
|
||||
<div class="card-body">
|
||||
<h2 class="text-center mb-4"><i class="ti ti-lock me-1"></i>Signage Admin</h2>
|
||||
<form method="post" action="{{ url_for('login') }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Benutzername</label>
|
||||
<input class="form-control" type="text" name="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Passwort</label>
|
||||
<input class="form-control" type="password" name="password">
|
||||
</div>
|
||||
<button class="btn btn-primary w-100" type="submit"><i class="ti ti-login me-1"></i>Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
86
templates/manage_staff.html
Normal file
86
templates/manage_staff.html
Normal file
@@ -0,0 +1,86 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-section-title">
|
||||
<h1>Bearbeiterverwaltung</h1>
|
||||
<p>Admins und Bearbeiter verwalten sowie Passwort-Resets ausloesen.</p>
|
||||
</div>
|
||||
|
||||
<div class="content-stack-wide">
|
||||
<div class="grid">
|
||||
<article class="card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title"><i class="ti ti-user-plus me-1"></i>Bearbeiter anlegen</h2>
|
||||
<form method="post" action="{{ url_for('manage_staff') }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Benutzername</label>
|
||||
<input class="form-control" type="text" name="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Vollstaendiger Name</label>
|
||||
<input class="form-control" type="text" name="full_name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rolle</label>
|
||||
<select class="form-select" name="role" required>
|
||||
<option value="staff">Bearbeiter</option>
|
||||
<option value="admin">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit"><i class="ti ti-user-plus me-1"></i>Bearbeiter speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="card-title"><i class="ti ti-users-group me-1"></i>Bearbeiterverwaltung</h2>
|
||||
</div>
|
||||
{% if staff_users %}
|
||||
<div class="table-responsive table-wrap">
|
||||
<table class="table table-vcenter card-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Benutzername</th>
|
||||
<th>Rolle</th>
|
||||
<th>Status</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for staff in staff_users %}
|
||||
<tr>
|
||||
<td>{{ staff.full_name }}</td>
|
||||
<td>{{ staff.username }}</td>
|
||||
<td>{{ staff.role }}</td>
|
||||
<td>
|
||||
{% if staff.must_set_password %}
|
||||
Passwort ausstehend
|
||||
{% else %}
|
||||
Aktiv
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<form method="post" action="{{ url_for('reset_staff_password', staff_id=staff.id) }}">
|
||||
<button class="btn btn-primary btn-sm" type="submit"><i class="ti ti-refresh me-1"></i>Passwort reset</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('delete_staff', staff_id=staff.id) }}" onsubmit="return confirm('Bearbeiter wirklich loeschen?');">
|
||||
<button class="btn btn-danger btn-sm" type="submit"><i class="ti ti-trash me-1"></i>Loeschen</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card-body"><p class="mb-0">Keine Bearbeiter vorhanden.</p></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
314
templates/print_transaction.html
Normal file
314
templates/print_transaction.html
Normal file
@@ -0,0 +1,314 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Druckbeleg</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
<style>
|
||||
@page {
|
||||
size: A4 portrait;
|
||||
margin: 10mm;
|
||||
}
|
||||
|
||||
:root {
|
||||
--ccm-bg: #f4f6f8;
|
||||
--ccm-text: #212121;
|
||||
--ccm-primary: #da002d;
|
||||
--ccm-primary-hover: #b00024;
|
||||
--ccm-header: #2b2f36;
|
||||
--ccm-surface: #ffffff;
|
||||
--ccm-border: #d9dee3;
|
||||
--ccm-muted: #6b7280;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Segoe UI", Arial, sans-serif;
|
||||
color: var(--ccm-text);
|
||||
background: var(--ccm-bg);
|
||||
}
|
||||
|
||||
.page {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.sheet {
|
||||
background: var(--ccm-surface);
|
||||
border: 1px solid var(--ccm-border);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 12px 30px rgba(43, 47, 54, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.topbar {
|
||||
background: linear-gradient(135deg, #da002d 0%, #b00024 100%);
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
.logo-row {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 1.25rem 1.5rem 0;
|
||||
background: linear-gradient(135deg, #da002d 0%, #b00024 100%);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
background: linear-gradient(135deg, #da002d 0%, #b00024 100%);
|
||||
color: #ffffff;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.print-logo {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 0.35rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.details {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.details th,
|
||||
.details td {
|
||||
text-align: left;
|
||||
padding: 0.75rem 0;
|
||||
border-bottom: 1px solid var(--ccm-border);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.details th {
|
||||
width: 30%;
|
||||
color: var(--ccm-muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.note-box {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 1.25rem;
|
||||
border-left: 4px solid var(--ccm-primary);
|
||||
background: rgba(218, 0, 45, 0.06);
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.signature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 2rem;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
.signature-box {
|
||||
min-height: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.signature-line {
|
||||
border-top: 1px solid var(--ccm-text);
|
||||
padding-top: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.signature-date {
|
||||
margin-top: 0.5rem;
|
||||
color: var(--ccm-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.print-actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background: var(--ccm-primary);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
background: var(--ccm-bg);
|
||||
color: var(--ccm-text);
|
||||
}
|
||||
|
||||
@media print {
|
||||
html,
|
||||
body {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.print-actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 100%;
|
||||
max-width: 190mm;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sheet {
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
min-height: 277mm;
|
||||
}
|
||||
|
||||
.print-logo {
|
||||
filter: brightness(0) saturate(100%);
|
||||
}
|
||||
|
||||
.topbar {
|
||||
padding: 0 8mm 8mm;
|
||||
}
|
||||
|
||||
.logo-row {
|
||||
padding: 0 8mm 6mm;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 8mm;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 8mm;
|
||||
}
|
||||
|
||||
.signature-grid {
|
||||
margin-top: 26mm;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.page {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.signature-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="page">
|
||||
<section class="sheet">
|
||||
<div class="topbar"></div>
|
||||
<div class="logo-row">
|
||||
<img class="print-logo" src="{{ url_for('static', filename='cancom.svg') }}" alt="CANCOM Logo">
|
||||
</div>
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
<h1>{{ action_labels[transaction.action] }} {{ asset_labels[transaction.asset_type] }}</h1>
|
||||
<div class="subtitle">{{ print_descriptions[transaction.action] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<table class="details">
|
||||
<tr>
|
||||
<th>Datum</th>
|
||||
<td>{{ transaction.created_at }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Anwender</th>
|
||||
<td>{{ transaction.full_name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>E-Mail</th>
|
||||
<td>{{ transaction.email or "-" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Abteilung</th>
|
||||
<td>{{ transaction.department or "-" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Medium</th>
|
||||
<td>{{ asset_labels[transaction.asset_type] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Kennung</th>
|
||||
<td>{{ transaction.asset_code }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Bearbeiter</th>
|
||||
<td>{{ transaction.handled_by or "-" }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="note-box">
|
||||
Mit den untenstehenden Unterschriften bestaetigen Bearbeiter und Anwender die korrekte {{ action_labels[transaction.action] | lower }} des angegebenen Mediums.
|
||||
</div>
|
||||
|
||||
<div class="signature-grid">
|
||||
<div class="signature-box">
|
||||
<div class="signature-line">{{ transaction.handled_by or "Bearbeiter" }}</div>
|
||||
<div class="signature-date">Datum: {{ transaction.created_at }}</div>
|
||||
</div>
|
||||
<div class="signature-box">
|
||||
<div class="signature-line">{{ transaction.full_name }}</div>
|
||||
<div class="signature-date">Datum: {{ transaction.created_at }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="print-actions">
|
||||
<a class="button button-primary" href="#" onclick="window.print(); return false;">Drucken</a>
|
||||
<a class="button button-secondary" href="{{ url_for('index') }}">Zur Uebersicht</a>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
33
templates/return_asset.html
Normal file
33
templates/return_asset.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-section-title">
|
||||
<h1>Rueckgabe</h1>
|
||||
<p>Ausgegebene Tuerchips und Parkkarten wieder zuruecknehmen.</p>
|
||||
</div>
|
||||
|
||||
<section class="card form-card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title"><i class="ti ti-arrow-back-up me-1"></i>Chip oder Parkkarte zuruecknehmen</h2>
|
||||
<form method="post" action="{{ url_for('return_asset') }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">User</label>
|
||||
<select class="form-select" name="user_id" required>
|
||||
<option value="">Bitte waehlen</option>
|
||||
{% for user in users %}
|
||||
<option value="{{ user.id }}">{{ user.full_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Typ</label>
|
||||
<select class="form-select" name="asset_type" required>
|
||||
<option value="chip">Tuerchip</option>
|
||||
<option value="parking_card">Parkkarte</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit"><i class="ti ti-check me-1"></i>Rueckgabe speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
24
templates/set_password.html
Normal file
24
templates/set_password.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-sm-8 col-md-6 col-lg-4">
|
||||
<section class="card card-md mt-4">
|
||||
<div class="card-body">
|
||||
<h2 class="text-center mb-4"><i class="ti ti-keyboard me-1"></i>Passwort vergeben</h2>
|
||||
<form method="post" action="{{ url_for('set_password') }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Neues Passwort</label>
|
||||
<input class="form-control" type="password" name="password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Passwort wiederholen</label>
|
||||
<input class="form-control" type="password" name="password_confirm" required>
|
||||
</div>
|
||||
<button class="btn btn-primary w-100" type="submit">Passwort speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user