Compare commits

...

5 Commits

Author SHA1 Message Date
Aiirondev_dev 0ef928efd8 slight fix of the scanner
Release Inventarsystem / release-docker (push) Successful in 2m14s
2026-08-16 15:50:48 +02:00
Aiirondev_dev 1299140823 Fix of the encryption processing with the Student class
Release Inventarsystem / release-docker (push) Successful in 2m14s
2026-08-16 13:17:20 +02:00
Aiirondev_dev ea48d5c28a decryption of the parsed fields
Release Inventarsystem / release-docker (push) Successful in 2m15s
2026-08-16 13:08:34 +02:00
Aiirondev_dev 514065f4af fixe of a list error
Release Inventarsystem / release-docker (push) Successful in 3m7s
2026-08-16 12:58:31 +02:00
Aiirondev_dev 90da8487f2 feat(student-cards): add class-specific PDF export with dynamic dropdown
Release Inventarsystem / release-docker (push) Successful in 2m16s
- Implement /student_card_class_barcode_download route to filter student cards by class
- Dynamically extract unique class names from the database for frontend selection
- Update student_cards_admin.html to replace the text input with a class selection dropdown
2026-08-15 18:10:05 +02:00
3 changed files with 368 additions and 133 deletions
+354 -132
View File
@@ -2177,7 +2177,7 @@ def _upload_student_cards_excel():
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
try:
db = client[cfg.MONGODB_DB]
student_cards = db['student_cards']
student_cards = list(db['student_cards'].find())
existing_ids.update(
str(card.get('AusweisId', '')).strip().upper()
for card in student_cards.find({}, {'AusweisId': 1})
@@ -4108,20 +4108,21 @@ def student_cards_admin():
app.logger.error(f"Error adding student card: {e}")
flash('Fehler beim Hinzufügen des Ausweises.', 'error')
# Get all student cards
# Get all student cards and decrypt them
all_cards = list(student_cards.find().sort('AusweisId', 1))
all_cards = [_decrypt_student_card_doc(card) for card in all_cards]
client.close()
raw_classes = set(card.get('Klasse', '').strip() for card in all_cards if card.get('Klasse'))
available_classes = sorted(list(raw_classes))
return render_template(
'student_cards_admin.html',
username=session['username'],
student_cards=all_cards,
student_cards=all_cards, # Hier all_cards übergeben
edit_mode=edit_mode,
form_data=form_data,
config={'default': cfg.STUDENT_DEFAULT_BORROW_DAYS},
library_module_enabled=cfg.MODULES.is_enabled('library'),
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards')
available_classes=available_classes, # Enthält nun die lesbaren Klassen
config=cfg.get_school_info()
)
@@ -4202,7 +4203,7 @@ def student_card_barcode_download():
if 'username' not in session:
return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library_view'))
@@ -4215,12 +4216,13 @@ def student_card_barcode_download():
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from reportlab.lib.colors import HexColor, white, black
from reportlab.pdfbase.pdfmetrics import stringWidth
from io import BytesIO
import barcode
from barcode.writer import ImageWriter
import tempfile
import os
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
student_cards = db['student_cards']
@@ -4230,7 +4232,7 @@ def student_card_barcode_download():
pdf_buffer = BytesIO()
c = canvas.Canvas(pdf_buffer, pagesize=A4)
page_width, page_height = A4
margin = 10 * mm
card_width = 88 * mm
@@ -4242,130 +4244,144 @@ def student_card_barcode_download():
y_start = page_height - margin
y_pos = y_start
col_idx = 0
# Professional color palette
header_color = HexColor("#0F172A") # Sehr dunkles blau
accent_color = HexColor("#2563EB") # Helles blau
light_bg = HexColor("#F8FAFC") # Sehr heller grau
card_bg_color = HexColor("#FFFFFF") # Weiß
text_dark = HexColor("#1E293B") # Dunkler text
text_gray = HexColor("#64748B") # Grauer text
light_bg = HexColor("#F8FAFC") # Sehr heller grau
card_bg_color = HexColor("#FFFFFF") # Weiß
text_dark = HexColor("#1E293B") # Dunkler text
text_gray = HexColor("#64748B") # Grauer text
for i, card in enumerate(all_cards):
if col_idx == 0 and i > 0:
y_pos -= (card_height + gap_y)
# New page if needed
if y_pos - card_height < margin:
c.showPage()
y_pos = y_start
col_idx = 0
x_pos = x_positions[col_idx]
# Card shadow effect (unter border)
c.setFillColor(HexColor("#E2E8F0"))
c.rect(x_pos + 0.5*mm, y_pos - card_height - 0.5*mm, card_width, card_height, fill=1, stroke=0)
c.rect(x_pos + 0.5 * mm, y_pos - card_height - 0.5 * mm, card_width, card_height, fill=1, stroke=0)
# Card background
c.setLineWidth(1)
c.setFillColor(card_bg_color)
c.setStrokeColor(HexColor("#CBD5E1"))
c.rect(x_pos, y_pos - card_height, card_width, card_height, fill=1, stroke=1)
# Left info section (38mm)
info_width = 38 * mm
c.setFillColor(light_bg)
c.rect(x_pos, y_pos - card_height, info_width, card_height, fill=1, stroke=0)
# Header bar
c.setFillColor(header_color)
c.rect(x_pos, y_pos - 10*mm, card_width, 10*mm, fill=1, stroke=0)
c.rect(x_pos, y_pos - 10 * mm, card_width, 10 * mm, fill=1, stroke=0)
# Header accent line
c.setStrokeColor(accent_color)
c.setLineWidth(2)
c.line(x_pos, y_pos - 10*mm, x_pos + card_width, y_pos - 10*mm)
c.line(x_pos, y_pos - 10 * mm, x_pos + card_width, y_pos - 10 * mm)
school_name = cfg.get_school_info()
school_name = school_name["name"]
# "SCHÜLERAUSWEIS" text in header
c.setFont("Helvetica-Bold", 9)
school_name_str = str(school_name["name"])
# Zwei-Zeilen-Header: 1. BIBLIOTHEKSAUSWEIS, 2. Schulname (dynamisch)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(white)
c.drawString(x_pos + 3*mm, y_pos - 6.5*mm, f"BIBLIOTHEKSAUSWEIS - {str(school_name)}")
c.drawString(x_pos + 3 * mm, y_pos - 4 * mm, "BIBLIOTHEKSAUSWEIS")
# Dynamische Anpassung des Schulnamens an die Breite
font_size = 8
while stringWidth(school_name_str, "Helvetica-Bold", font_size) > (card_width - 6 * mm) and font_size > 4:
font_size -= 0.5
c.setFont("Helvetica-Bold", font_size)
c.drawString(x_pos + 3 * mm, y_pos - 8 * mm, school_name_str)
# Student name - large and bold
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 10)
name = card['SchülerName'][:20]
c.drawString(x_pos + 3*mm, y_pos - 14*mm, name)
c.drawString(x_pos + 3 * mm, y_pos - 16 * mm, name)
# ID with label
c.setFont("Helvetica", 8)
c.setFillColor(text_gray)
c.drawString(x_pos + 3*mm, y_pos - 18*mm, "Ausweis ID:")
c.drawString(x_pos + 3 * mm, y_pos - 20 * mm, "Ausweis ID:")
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 9)
c.drawString(x_pos + 3*mm, y_pos - 21*mm, str(card['AusweisId']))
c.drawString(x_pos + 3 * mm, y_pos - 23 * mm, str(card['AusweisId']))
# Class with label
if card.get('Klasse'):
c.setFont("Helvetica", 8)
c.setFillColor(text_gray)
c.drawString(x_pos + 3*mm, y_pos - 25*mm, "Klasse:")
c.drawString(x_pos + 3 * mm, y_pos - 27 * mm, "Klasse:")
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 9)
c.drawString(x_pos + 3*mm, y_pos - 28*mm, card['Klasse'])
c.drawString(x_pos + 3*mm, y_pos - 31*mm, "-")
c.drawString(x_pos + 3*mm, y_pos - 34*mm, "-")
c.drawString(x_pos + 3*mm, y_pos - 37*mm, "-")
c.drawString(x_pos + 3*mm, y_pos - 40*mm, "-")
c.drawString(x_pos + 3 * mm, y_pos - 30 * mm, card['Klasse'])
c.drawString(x_pos + 3 * mm, y_pos - 33 * mm, "-")
c.drawString(x_pos + 3 * mm, y_pos - 36 * mm, "-")
c.drawString(x_pos + 3 * mm, y_pos - 39 * mm, "-")
c.drawString(x_pos + 3 * mm, y_pos - 42 * mm, "-")
# Right barcode section with border highlight
barcode_x_start = x_pos + info_width + 1*mm
barcode_x_start = x_pos + info_width + 1 * mm
c.setFillColor(accent_color)
c.setLineWidth(0)
c.rect(barcode_x_start - 1*mm, y_pos - card_height,
card_width - info_width + 1*mm, 2*mm, fill=1, stroke=0)
# Generate and add larger barcode
c.rect(barcode_x_start - 1 * mm, y_pos - card_height,
card_width - info_width + 1 * mm, 2 * mm, fill=1, stroke=0)
# Generate and add dynamically sized barcode
try:
temp_dir = tempfile.gettempdir()
barcode_path = os.path.join(temp_dir, f"barcode_{card['AusweisId']}")
# Generate barcode with higher module width for better scanning
# ImageWriter Settings für maximalen Platz und Lesbarkeit
options = {
'write_text': False, # Text entfernen (spart Platz)
'quiet_zone': 1.0, # Minimaler Rand
'dpi': 300 # Hohe Auflösung
}
barcode_obj = barcode.get('code128', str(card['AusweisId']), writer=ImageWriter())
barcode_obj.save(barcode_path)
barcode_obj.save(barcode_path, options=options)
barcode_file = f"{barcode_path}.png"
if os.path.exists(barcode_file):
# Larger barcode taking up most of right section
barcode_width = (card_width - info_width - 4*mm)
barcode_height = 16*mm
barcode_y = y_pos - card_height + (card_height - barcode_height) / 2 + 2*mm
c.drawImage(barcode_file,
barcode_x_start + 1*mm,
barcode_y,
width=barcode_width,
height=barcode_height,
preserveAspectRatio=True)
# Barcode über den fast kompletten restlichen Bereich strecken
barcode_width = (card_width - info_width - 4 * mm)
barcode_height = card_height - 15 * mm
barcode_y = y_pos - card_height + 2.5 * mm
c.drawImage(barcode_file,
barcode_x_start + 1 * mm,
barcode_y,
width=barcode_width,
height=barcode_height,
preserveAspectRatio=False) # Strecken erlauben für maximalen Scanbereich
os.remove(barcode_file)
else:
raise Exception("Barcode file not created")
except Exception as e:
c.setFont("Helvetica", 7)
c.setFillColor(HexColor("#DC2626"))
c.drawString(barcode_x_start + 2*mm, y_pos - card_height + 20*mm, "⚠️ Barcode Error")
c.drawString(barcode_x_start + 2 * mm, y_pos - card_height + 20 * mm, "⚠️ Barcode Error")
col_idx += 1
if col_idx >= cols:
col_idx = 0
c.save()
pdf_buffer.seek(0)
return send_file(
pdf_buffer,
mimetype='application/pdf',
@@ -4378,6 +4394,196 @@ def student_card_barcode_download():
return redirect(url_for('student_cards_admin'))
@app.route('/student_card_class_barcode_download', methods=['GET'])
def student_card_class_barcode_download():
"""
Download PDF with student card barcodes filtered by a specific class from dropdown.
"""
from flask import request, session, redirect, url_for, send_file, flash
if 'username' not in session:
return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library_view'))
class_name = request.args.get('class_name', '').strip()
if not class_name:
flash('Bitte wählen Sie eine Klasse aus.', 'error')
return redirect(url_for('student_cards_admin'))
try:
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from reportlab.lib.colors import HexColor, white
from reportlab.pdfbase.pdfmetrics import stringWidth
from io import BytesIO
import barcode
from barcode.writer import ImageWriter
import tempfile
import os
import datetime
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
student_cards = db['student_cards']
# 1. Alle Ausweise laden
all_cards = list(student_cards.find().sort('AusweisId', 1))
client.close()
# 2. Alle entschlüsseln
decrypted_cards = [_decrypt_student_card_doc(card) for card in all_cards]
# 3. Im Speicher nach der Klartext-Klasse filtern
class_cards = [
card for card in decrypted_cards
if card.get('Klasse', '').strip() == class_name
]
if not class_cards:
flash(f'Keine Ausweise für die Klasse "{class_name}" gefunden.', 'error')
return redirect(url_for('student_cards_admin'))
pdf_buffer = BytesIO()
c = canvas.Canvas(pdf_buffer, pagesize=A4)
page_width, page_height = A4
margin = 10 * mm
card_width = 88 * mm
card_height = 56 * mm
cols = 2
gap_x = 8 * mm
gap_y = 8 * mm
x_positions = [margin, margin + card_width + gap_x]
y_start = page_height - margin
y_pos = y_start
col_idx = 0
header_color = HexColor("#0F172A")
accent_color = HexColor("#2563EB")
light_bg = HexColor("#F8FAFC")
card_bg_color = HexColor("#FFFFFF")
text_dark = HexColor("#1E293B")
text_gray = HexColor("#64748B")
for i, card in enumerate(class_cards):
if col_idx == 0 and i > 0:
y_pos -= (card_height + gap_y)
if y_pos - card_height < margin:
c.showPage()
y_pos = y_start
col_idx = 0
x_pos = x_positions[col_idx]
c.setFillColor(HexColor("#E2E8F0"))
c.rect(x_pos + 0.5 * mm, y_pos - card_height - 0.5 * mm, card_width, card_height, fill=1, stroke=0)
c.setLineWidth(1)
c.setFillColor(card_bg_color)
c.setStrokeColor(HexColor("#CBD5E1"))
c.rect(x_pos, y_pos - card_height, card_width, card_height, fill=1, stroke=1)
info_width = 38 * mm
c.setFillColor(light_bg)
c.rect(x_pos, y_pos - card_height, info_width, card_height, fill=1, stroke=0)
c.setFillColor(header_color)
c.rect(x_pos, y_pos - 10 * mm, card_width, 10 * mm, fill=1, stroke=0)
c.setStrokeColor(accent_color)
c.setLineWidth(2)
c.line(x_pos, y_pos - 10 * mm, x_pos + card_width, y_pos - 10 * mm)
school_name = cfg.get_school_info()
school_name_str = str(school_name["name"])
c.setFont("Helvetica-Bold", 8)
c.setFillColor(white)
c.drawString(x_pos + 3 * mm, y_pos - 4 * mm, "BIBLIOTHEKSAUSWEIS")
font_size = 8
while stringWidth(school_name_str, "Helvetica-Bold", font_size) > (card_width - 6 * mm) and font_size > 4:
font_size -= 0.5
c.setFont("Helvetica-Bold", font_size)
c.drawString(x_pos + 3 * mm, y_pos - 8 * mm, school_name_str)
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 10)
name = card.get('SchülerName', '')[:20]
c.drawString(x_pos + 3 * mm, y_pos - 16 * mm, name)
c.setFont("Helvetica", 8)
c.setFillColor(text_gray)
c.drawString(x_pos + 3 * mm, y_pos - 20 * mm, "Ausweis ID:")
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 9)
c.drawString(x_pos + 3 * mm, y_pos - 23 * mm, str(card.get('AusweisId', '')))
if card.get('Klasse'):
c.setFont("Helvetica", 8)
c.setFillColor(text_gray)
c.drawString(x_pos + 3 * mm, y_pos - 27 * mm, "Klasse:")
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 9)
c.drawString(x_pos + 3 * mm, y_pos - 30 * mm, card['Klasse'])
barcode_x_start = x_pos + info_width + 1 * mm
c.setFillColor(accent_color)
c.setLineWidth(0)
c.rect(barcode_x_start - 1 * mm, y_pos - card_height, card_width - info_width + 1 * mm, 2 * mm, fill=1,
stroke=0)
try:
temp_dir = tempfile.gettempdir()
barcode_path = os.path.join(temp_dir, f"barcode_{card.get('AusweisId', i)}")
options = {
'write_text': False,
'quiet_zone': 1.0,
'dpi': 300
}
barcode_obj = barcode.get('code128', str(card.get('AusweisId', '')), writer=ImageWriter())
barcode_obj.save(barcode_path, options=options)
barcode_file = f"{barcode_path}.png"
if os.path.exists(barcode_file):
barcode_width = (card_width - info_width - 4 * mm)
barcode_height = card_height - 15 * mm
barcode_y = y_pos - card_height + 2.5 * mm
c.drawImage(barcode_file, barcode_x_start + 1 * mm, barcode_y,
width=barcode_width, height=barcode_height, preserveAspectRatio=False)
os.remove(barcode_file)
except Exception as e:
pass
col_idx += 1
if col_idx >= cols:
col_idx = 0
c.save()
pdf_buffer.seek(0)
filename = f'ausweise_klasse_{class_name.replace(" ", "_")}_{datetime.datetime.now().strftime("%Y%m%d")}.pdf'
return send_file(
pdf_buffer,
mimetype='application/pdf',
as_attachment=True,
download_name=filename
)
except Exception as e:
flash('Fehler beim PDF-Download', 'error')
return redirect(url_for('student_cards_admin'))
@app.route('/student_card_single_barcode_download/<card_id>', methods=['GET'])
def student_card_single_barcode_download(card_id):
"""
@@ -4386,7 +4592,7 @@ def student_card_single_barcode_download(card_id):
if 'username' not in session:
return redirect(url_for('login'))
current_permissions = us.get_effective_permissions(session['username'])
if not current_permissions['actions'].get('can_manage_users', False):
flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error')
return redirect(url_for('library_view'))
@@ -4399,148 +4605,165 @@ def student_card_single_barcode_download(card_id):
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from reportlab.lib.colors import HexColor, white, black
from reportlab.pdfbase.pdfmetrics import stringWidth
from io import BytesIO
from bson.objectid import ObjectId
import barcode
from barcode.writer import ImageWriter
import tempfile
import os
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB]
student_cards = db['student_cards']
card = student_cards.find_one({'_id': ObjectId(card_id)})
card = _decrypt_student_card_doc(card)
client.close()
if not card:
flash('Ausweis nicht gefunden.', 'error')
return redirect(url_for('student_cards_admin'))
pdf_buffer = BytesIO()
c = canvas.Canvas(pdf_buffer, pagesize=A4)
page_width, page_height = A4
margin = 20 * mm
card_width = 105 * mm
card_height = 70 * mm
x_pos = (page_width - card_width) / 2
y_pos = (page_height - card_height) / 2
# Korrektur des y_pos auf die OBERKANTE, damit die nachfolgenden Rechnungen bündig sind
y_pos = (page_height + card_height) / 2
# Professional color palette
header_color = HexColor("#0F172A") # Sehr dunkles blau
accent_color = HexColor("#2563EB") # Helles blau
light_bg = HexColor("#F8FAFC") # Sehr heller grau
card_bg_color = HexColor("#FFFFFF") # Weiß
text_dark = HexColor("#1E293B") # Dunkler text
text_gray = HexColor("#64748B") # Grauer text
header_color = HexColor("#0F172A")
accent_color = HexColor("#2563EB")
light_bg = HexColor("#F8FAFC")
card_bg_color = HexColor("#FFFFFF")
text_dark = HexColor("#1E293B")
text_gray = HexColor("#64748B")
# Card shadow effect
c.setFillColor(HexColor("#E2E8F0"))
c.rect(x_pos + 1*mm, y_pos - card_height - 1*mm, card_width, card_height, fill=1, stroke=0)
c.rect(x_pos + 1 * mm, y_pos - card_height - 1 * mm, card_width, card_height, fill=1, stroke=0)
# Card background
c.setLineWidth(1.5)
c.setFillColor(card_bg_color)
c.setStrokeColor(HexColor("#CBD5E1"))
c.rect(x_pos, y_pos, card_width, card_height, fill=1, stroke=1)
c.rect(x_pos, y_pos - card_height, card_width, card_height, fill=1, stroke=1)
# Left info section (50mm)
info_width = 50 * mm
c.setFillColor(light_bg)
c.rect(x_pos, y_pos - card_height, info_width, card_height, fill=1, stroke=0)
# Vertical divider line
c.setStrokeColor(accent_color)
c.setLineWidth(3)
c.line(x_pos + info_width, y_pos - card_height, x_pos + info_width, y_pos)
# Header bar
c.setFillColor(header_color)
c.rect(x_pos, y_pos - 10*mm, card_width, 10*mm, fill=1, stroke=0)
c.rect(x_pos, y_pos - 10 * mm, card_width, 10 * mm, fill=1, stroke=0)
# Header accent line
c.setStrokeColor(accent_color)
c.setLineWidth(2.5)
c.line(x_pos, y_pos - 10*mm, x_pos + card_width, y_pos - 10*mm)
c.line(x_pos, y_pos - 10 * mm, x_pos + card_width, y_pos - 10 * mm)
school_name = cfg.get_school_info()
school_name = school_name["name"]
# "SCHÜLERAUSWEIS" text in header
c.setFont("Helvetica-Bold", 11)
school_name_str = str(school_name["name"])
# Zwei-Zeilen-Header
c.setFont("Helvetica-Bold", 10)
c.setFillColor(white)
c.drawString(x_pos + 4*mm, y_pos - 6.5*mm, f"BIBLIOTHEKSAUSWEIS - {str(school_name)}")
c.drawString(x_pos + 4 * mm, y_pos - 4.5 * mm, "BIBLIOTHEKSAUSWEIS")
# Dynamische Größe für den Schulnamen
font_size = 10
while stringWidth(school_name_str, "Helvetica-Bold", font_size) > (card_width - 8 * mm) and font_size > 4:
font_size -= 0.5
c.setFont("Helvetica-Bold", font_size)
c.drawString(x_pos + 4 * mm, y_pos - 8.5 * mm, school_name_str)
# Student name - large and prominent
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 12)
name = card['SchülerName'][:25]
c.drawString(x_pos + 4*mm, y_pos - 16*mm, name)
c.drawString(x_pos + 4 * mm, y_pos - 18 * mm, name)
# ID section with label
c.setFont("Helvetica", 9)
c.setFillColor(text_gray)
c.drawString(x_pos + 4*mm, y_pos - 21*mm, "Ausweis-ID:")
c.drawString(x_pos + 4 * mm, y_pos - 24 * mm, "Ausweis-ID:")
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 11)
c.drawString(x_pos + 4*mm, y_pos - 25*mm, str(card['AusweisId']))
c.drawString(x_pos + 4 * mm, y_pos - 28 * mm, str(card['AusweisId']))
# Class section
if card.get('Klasse'):
c.setFont("Helvetica", 9)
c.setFillColor(text_gray)
c.drawString(x_pos + 4*mm, y_pos - 30*mm, "Klasse:")
c.drawString(x_pos + 4 * mm, y_pos - 34 * mm, "Klasse:")
c.setFillColor(text_dark)
c.setFont("Helvetica-Bold", 11)
c.drawString(x_pos + 4*mm, y_pos - 34*mm, card['Klasse'])
c.drawString(x_pos + 4 * mm, y_pos - 38 * mm, card['Klasse'])
# Barcode section - right side with blue accent
barcode_x_start = x_pos + info_width + 3*mm
barcode_x_start = x_pos + info_width + 3 * mm
c.setFillColor(accent_color)
c.setLineWidth(0)
c.rect(x_pos + info_width, y_pos - card_height,
card_width - info_width, 3*mm, fill=1, stroke=0)
# Generate and add large barcode
c.rect(x_pos + info_width, y_pos - card_height,
card_width - info_width, 3 * mm, fill=1, stroke=0)
# Generate and add large dynamically sized barcode
try:
temp_dir = tempfile.gettempdir()
barcode_path = os.path.join(temp_dir, f"barcode_{card['AusweisId']}")
# Generate barcode with better sizing
# Options anpassen für Scanbarkeit
options = {
'write_text': False,
'quiet_zone': 1.0,
'dpi': 300
}
barcode_obj = barcode.get('code128', str(card['AusweisId']), writer=ImageWriter())
barcode_obj.save(barcode_path)
barcode_obj.save(barcode_path, options=options)
barcode_file = f"{barcode_path}.png"
if os.path.exists(barcode_file):
# Large barcode on right side
barcode_width = (card_width - info_width - 8*mm)
barcode_height = 20*mm
barcode_y_offset = (card_height - 10*mm - barcode_height) / 2
c.drawImage(barcode_file,
barcode_x_start,
y_pos - card_height + barcode_y_offset + 5*mm,
width=barcode_width,
height=barcode_height,
preserveAspectRatio=True)
# Den gesamten Platz auf der rechten Seite ausnutzen
barcode_width = (card_width - info_width - 8 * mm)
barcode_height = card_height - 18 * mm
barcode_y = y_pos - card_height + 5 * mm
c.drawImage(barcode_file,
barcode_x_start + 1 * mm,
barcode_y,
width=barcode_width,
height=barcode_height,
preserveAspectRatio=False) # Ausdehnung in alle Richtungen
os.remove(barcode_file)
else:
raise Exception("Barcode file not created")
except Exception as e:
c.setFont("Helvetica", 9)
c.setFillColor(HexColor("#DC2626"))
c.drawString(barcode_x_start + 2*mm, y_pos - card_height + 25*mm, "⚠️ Barcode Error")
c.drawString(barcode_x_start + 2 * mm, y_pos - card_height + 25 * mm, "⚠️ Barcode Error")
# Bottom info line
c.setStrokeColor(HexColor("#CBD5E1"))
c.setLineWidth(0.5)
c.line(x_pos, y_pos - card_height + 3*mm, x_pos + card_width, y_pos - card_height + 3*mm)
c.line(x_pos, y_pos - card_height + 3 * mm, x_pos + card_width, y_pos - card_height + 3 * mm)
c.save()
pdf_buffer.seek(0)
return send_file(
pdf_buffer,
mimetype='application/pdf',
@@ -4552,7 +4775,6 @@ def student_card_single_barcode_download(card_id):
flash('Fehler beim PDF-Download', 'error')
return redirect(url_for('student_cards_admin'))
@app.route('/login', methods=['GET', 'POST'])
def login():
"""
+4 -1
View File
@@ -816,12 +816,15 @@
if (readerWrap) readerWrap.style.display = 'block';
setScanStatus('Initializing camera...', 'warn');
const scannerContainer = document.querySelector('.library-scan-reader-wrap');
scannerContainer.style.display = 'block';
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector('#library-scanner-container'),
target: document.querySelector('.library-scan-reader'),
constraints: {
width: 640,
height: 480,
+10
View File
@@ -232,6 +232,16 @@
<h1>📚 Bibliotheksausweise (Bibliothek)</h1>
</div>
<div class="export-buttons">
<form method="GET" action="{{ url_for('student_card_class_barcode_download') }}" style="display: inline-flex; gap: 5px; align-items: center; background: white; padding: 2px; border-radius: 4px; border: 1px solid #ddd;">
<select name="class_name" required style="border: none; padding: 8px; outline: none; font-size: 14px; background: transparent; cursor: pointer;">
<option value="" disabled selected>-- Klasse wählen --</option>
{% for cls in available_classes %}
<option value="{{ cls }}">{{ cls }}</option>
{% endfor %}
</select>
<button type="submit" class="btn-print" style="background: #17a2b8; padding: 8px 12px; margin: 0;">📤 PDF</button>
</form>
<a href="{{ url_for('student_card_barcode_download') }}" class="btn-print" style="background: #28a745;">📥 Alle Ausweise (PDF)</a>
<a href="{{ url_for('library_admin') }}" class="btn btn-primary">← Zur Bibliotheks-Upload</a>
</div>