slight correction to also account for name and last_name
Release Inventarsystem / release-docker (push) Successful in 2m16s

This commit is contained in:
2026-08-12 16:09:52 +02:00
parent 5ba0944faf
commit cdf7b9c45d
2 changed files with 313 additions and 173 deletions
+112 -59
View File
@@ -7865,6 +7865,82 @@ def register():
permission_page_options=PERMISSION_PAGE_OPTIONS
)
def parse_csv_users(file_bytes):
"""
Liest CSV-Dateien extrem robust ein:
- Erkennt automatisch UTF-8, UTF-8-SIG und Latin-1 (für Excel-Umlaute).
- Erkennt automatisch das Trennzeichen (; oder , oder Tab).
- Erkennt Vor- und Nachname unabhängig von der Spaltenreihenfolge und Headernamen.
"""
# 1. Dekodierung mit Fallback für deutsche Excel-Dateien
try:
content = file_bytes.decode('utf-8-sig')
except UnicodeDecodeError:
content = file_bytes.decode('latin-1')
lines = [line.strip() for line in content.splitlines() if line.strip()]
if not lines:
return []
# 2. Trennzeichen ermitteln (; , oder \t)
first_line = lines[0]
if first_line.count(';') >= first_line.count(','):
delimiter = ';'
elif first_line.count('\t') > first_line.count(','):
delimiter = '\t'
else:
delimiter = ','
stream = io.StringIO(content, newline=None)
reader = csv.DictReader(stream, delimiter=delimiter)
parsed_users = []
# 3. Auswertung mit DictReader (wenn Header vorhanden sind)
if reader.fieldnames:
for row in reader:
# Keys normalisieren (kleingeschrieben, ohne Leerzeichen)
cleaned_row = {str(k).strip().lower(): str(v).strip() for k, v in row.items() if k and v}
# Dynamisches Mapping für Vornamen
name = (
cleaned_row.get('vorname') or
cleaned_row.get('first_name') or
cleaned_row.get('firstname') or
cleaned_row.get('name') or ''
)
# Dynamisches Mapping für Nachnamen
last_name = (
cleaned_row.get('nachname') or
cleaned_row.get('last_name') or
cleaned_row.get('lastname') or
cleaned_row.get('surname') or
cleaned_row.get('familienname') or ''
)
# Falls Header nicht erkannt wurden, aber Werte vorhanden sind (z.B. CSV ohne Header)
if not name and not last_name:
vals = [str(v).strip() for v in row.values() if v]
if len(vals) >= 2:
name, last_name = vals[0], vals[1]
if name or last_name:
parsed_users.append({'name': name, 'last_name': last_name})
else:
# Fallback für dateien ohne Header
stream.seek(0)
raw_reader = csv.reader(stream, delimiter=delimiter)
for row in raw_reader:
clean_row = [str(cell).strip() for cell in row if str(cell).strip()]
if len(clean_row) >= 2:
# Header-Zeilen überspringen
if clean_row[0].lower() in ['vorname', 'first_name', 'name'] and clean_row[1].lower() in ['nachname', 'last_name']:
continue
parsed_users.append({'name': clean_row[0], 'last_name': clean_row[1]})
return parsed_users
def generate_compliant_password(length=16):
lowers = string.ascii_lowercase
uppers = string.ascii_uppercase
@@ -7983,77 +8059,54 @@ def register_csv():
permission_preset = (request.form.get('permission_preset') or 'standard_user').strip()
# 1. CSV über die robuste Funktion einlesen
file_bytes = file.stream.read()
raw_users = parse_csv_users(file_bytes)
if not raw_users:
flash('Keine gültigen Benutzer in der CSV-Datei gefunden. Bitte prüfen Sie das Format.', 'error')
return redirect(url_for('register'))
created_users = []
try:
# Read file stream handling UTF-8 and BOM (Excel exports)
file_stream = io.StringIO(file.stream.read().decode("utf-8-sig"), newline=None)
for entry in raw_users:
name = entry['name']
last_name = entry['last_name']
# Sniff delimiter (comma or semicolon)
sample = file_stream.read(2048)
file_stream.seek(0)
delimiter = ';' if ';' in sample else ','
if not name or not last_name:
continue
reader = csv.reader(file_stream, delimiter=delimiter)
# Benutzernamen & Passwort generieren
username = us.build_unique_username_from_name(name, last_name)
password = generate_compliant_password(16)
for row_num, row in enumerate(reader, start=1):
if not row or all(field.strip() == '' for field in row):
continue # Skip empty rows
# In DB speichern
success = us.add_user(
username=username,
password=password,
name=name,
last_name=last_name,
is_student=False,
student_card_id=None,
max_borrow_days=None,
permission_preset=permission_preset,
)
# Clean entries
row = [field.strip() for field in row]
# Header row detection (Skip row if it looks like "Name, Nachname")
if row_num == 1 and ('name' in row[0].lower() or 'vorname' in row[0].lower()):
continue
if len(row) < 2:
continue
name = row[0]
last_name = row[1]
if not name or not last_name:
continue
# 1. Generate Username via backend logic
username = us.build_unique_username_from_name(name, last_name)
# 2. Generate Random Secure Password
password = generate_compliant_password(16)
# 3. Add User to DB
success = us.add_user(
username=username,
password=password,
name=name,
last_name=last_name,
is_student=False,
student_card_id=None,
max_borrow_days=None,
permission_preset=permission_preset,
)
if success:
created_users.append({
'name': name,
'last_name': last_name,
'username': username,
'password': password
})
except Exception as e:
flash(f'Fehler beim Verarbeiten der CSV-Datei: {str(e)}', 'error')
return redirect(url_for('register'))
if success:
created_users.append({
'name': name,
'last_name': last_name,
'username': username,
'password': password
})
if not created_users:
flash('Keine gültigen Benutzer in der CSV-Datei gefunden.', 'error')
flash('Fehler beim Erstellen der Benutzer aus der CSV.', 'error')
return redirect(url_for('register'))
# Generate PDF
# PDF mit Zugangsdaten generieren
pdf_buffer = generate_credentials_pdf(created_users)
# Return downloadable PDF file
return send_file(
pdf_buffer,
as_attachment=True,