implementation of the user .csv importing
Release Inventarsystem / release-docker (push) Successful in 2m16s
Release Inventarsystem / release-docker (push) Successful in 2m16s
This commit is contained in:
+200
@@ -27,6 +27,10 @@ import os
|
||||
import sys
|
||||
from bs4 import BeautifulSoup
|
||||
from gridfs import GridFS
|
||||
import string
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib import colors
|
||||
|
||||
# Ensure imports work regardless of whether gunicorn starts in /app or /app/Web.
|
||||
_CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -7861,6 +7865,202 @@ def register():
|
||||
permission_page_options=PERMISSION_PAGE_OPTIONS
|
||||
)
|
||||
|
||||
def generate_compliant_password(length=16):
|
||||
lowers = string.ascii_lowercase
|
||||
uppers = string.ascii_uppercase
|
||||
digits = string.digits
|
||||
symbols = "!@#$%^&*()_+~|}{[]:;?><,.-="
|
||||
|
||||
# Ensure at least one character from each required category
|
||||
pwd = [
|
||||
secrets.choice(lowers),
|
||||
secrets.choice(uppers),
|
||||
secrets.choice(digits),
|
||||
secrets.choice(symbols)
|
||||
]
|
||||
all_chars = lowers + uppers + digits + symbols
|
||||
pwd += [secrets.choice(all_chars) for _ in range(length - 4)]
|
||||
|
||||
# Shuffle so guaranteed types aren't always at the start
|
||||
secrets.SystemRandom().shuffle(pwd)
|
||||
return "".join(pwd)
|
||||
|
||||
def generate_credentials_pdf(created_users):
|
||||
"""
|
||||
Creates a PDF in memory with 2 user credential cards per A4 page.
|
||||
created_users: list of dicts [{'name': ..., 'last_name': ..., 'username': ..., 'password': ...}]
|
||||
"""
|
||||
buffer = io.BytesIO()
|
||||
pdf = canvas.Canvas(buffer, pagesize=A4)
|
||||
width, height = A4 # 595.27 x 841.89 points
|
||||
card_height = height / 2.0 # Split page into 2 equal halves
|
||||
|
||||
for i, user in enumerate(created_users):
|
||||
page_slot = i % 2 # 0 = Top half, 1 = Bottom half
|
||||
|
||||
# If starting a new page (except for the very first item)
|
||||
if i > 0 and page_slot == 0:
|
||||
pdf.showPage()
|
||||
|
||||
# Calculate Y offset for card position
|
||||
y_offset = height - (page_slot + 1) * card_height
|
||||
|
||||
# Card Container Box
|
||||
margin = 35
|
||||
box_x = margin
|
||||
box_y = y_offset + margin
|
||||
box_w = width - (2 * margin)
|
||||
box_h = card_height - (2 * margin)
|
||||
|
||||
# Outer Border
|
||||
pdf.setStrokeColor(colors.HexColor('#CBD5E1'))
|
||||
pdf.setLineWidth(1)
|
||||
pdf.rect(box_x, box_y, box_w, box_h, fill=0)
|
||||
|
||||
# Header Banner inside Card
|
||||
pdf.setFillColor(colors.HexColor('#1E293B'))
|
||||
pdf.rect(box_x, box_y + box_h - 45, box_w, 45, fill=1, stroke=0)
|
||||
|
||||
# Header Title Text
|
||||
pdf.setFillColor(colors.white)
|
||||
pdf.setFont("Helvetica-Bold", 14)
|
||||
pdf.drawString(box_x + 20, box_y + box_h - 28, "Zugangsdaten / Account Credentials")
|
||||
|
||||
# User Info Details
|
||||
content_y = box_y + box_h - 80
|
||||
|
||||
# Name
|
||||
pdf.setFillColor(colors.HexColor('#0F172A'))
|
||||
pdf.setFont("Helvetica-Bold", 12)
|
||||
pdf.drawString(box_x + 25, content_y, f"Name: {user['name']} {user['last_name']}")
|
||||
|
||||
# Username
|
||||
content_y -= 35
|
||||
pdf.setFont("Helvetica", 11)
|
||||
pdf.setFillColor(colors.HexColor('#475569'))
|
||||
pdf.drawString(box_x + 25, content_y, "Benutzername:")
|
||||
pdf.setFont("Helvetica-Bold", 13)
|
||||
pdf.setFillColor(colors.HexColor('#0F172A'))
|
||||
pdf.drawString(box_x + 160, content_y, user['username'])
|
||||
|
||||
# Password
|
||||
content_y -= 30
|
||||
pdf.setFont("Helvetica", 11)
|
||||
pdf.setFillColor(colors.HexColor('#475569'))
|
||||
pdf.drawString(box_x + 25, content_y, "Passwort:")
|
||||
pdf.setFont("Courier-Bold", 13)
|
||||
pdf.setFillColor(colors.HexColor('#0F172A'))
|
||||
pdf.drawString(box_x + 160, content_y, user['password'])
|
||||
|
||||
# Security Footer Note
|
||||
content_y -= 45
|
||||
pdf.setFont("Helvetica-Oblique", 9)
|
||||
pdf.setFillColor(colors.HexColor('#64748B'))
|
||||
pdf.drawString(box_x + 25, content_y, "Hinweis: Bitte ändern Sie Ihr Passwort nach der ersten Anmeldung.")
|
||||
|
||||
# Dashed Cut Line between top and bottom cards
|
||||
if page_slot == 0 and i < len(created_users) - 1:
|
||||
pdf.setDash(4, 4)
|
||||
pdf.setStrokeColor(colors.HexColor('#94A3B8'))
|
||||
pdf.line(0, card_height, width, card_height)
|
||||
pdf.setDash() # Reset dash
|
||||
|
||||
pdf.save()
|
||||
buffer.seek(0)
|
||||
return buffer
|
||||
|
||||
|
||||
@app.route('/register/csv', methods=['POST'])
|
||||
def register_csv():
|
||||
if 'username' not in session:
|
||||
flash('Ihnen ist es nicht gestattet, diese Aktion auszuführen.', 'error')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
file = request.files.get('csv_file')
|
||||
if not file or not file.filename.endswith('.csv'):
|
||||
flash('Bitte laden Sie eine gültige CSV-Datei hoch.', 'error')
|
||||
return redirect(url_for('register'))
|
||||
|
||||
permission_preset = (request.form.get('permission_preset') or 'standard_user').strip()
|
||||
|
||||
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)
|
||||
|
||||
# Sniff delimiter (comma or semicolon)
|
||||
sample = file_stream.read(2048)
|
||||
file_stream.seek(0)
|
||||
delimiter = ';' if ';' in sample else ','
|
||||
|
||||
reader = csv.reader(file_stream, delimiter=delimiter)
|
||||
|
||||
for row_num, row in enumerate(reader, start=1):
|
||||
if not row or all(field.strip() == '' for field in row):
|
||||
continue # Skip empty rows
|
||||
|
||||
# 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 not created_users:
|
||||
flash('Keine gültigen Benutzer in der CSV-Datei gefunden.', 'error')
|
||||
return redirect(url_for('register'))
|
||||
|
||||
# Generate PDF
|
||||
pdf_buffer = generate_credentials_pdf(created_users)
|
||||
|
||||
# Return downloadable PDF file
|
||||
return send_file(
|
||||
pdf_buffer,
|
||||
as_attachment=True,
|
||||
download_name='benutzer_zugangsdaten.pdf',
|
||||
mimetype='application/pdf'
|
||||
)
|
||||
|
||||
@app.route('/user_del', methods=['GET'])
|
||||
def user_del():
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user