Merge remote-tracking branch 'origin/main'
Release Inventarsystem / release-docker (push) Successful in 2m15s

This commit is contained in:
2026-08-16 20:06:12 +02:00
2 changed files with 88 additions and 66 deletions
+17 -16
View File
@@ -2177,7 +2177,7 @@ def _upload_student_cards_excel():
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
try: try:
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
student_cards = db['student_cards'] student_cards = list(db['student_cards'].find())
existing_ids.update( existing_ids.update(
str(card.get('AusweisId', '')).strip().upper() str(card.get('AusweisId', '')).strip().upper()
for card in student_cards.find({}, {'AusweisId': 1}) for card in student_cards.find({}, {'AusweisId': 1})
@@ -4108,28 +4108,20 @@ def student_cards_admin():
app.logger.error(f"Error adding student card: {e}") app.logger.error(f"Error adding student card: {e}")
flash('Fehler beim Hinzufügen des Ausweises.', 'error') 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 = list(student_cards.find().sort('AusweisId', 1))
all_cards = [_decrypt_student_card_doc(card) for card in all_cards] all_cards = [_decrypt_student_card_doc(card) for card in all_cards]
client.close() client.close()
# Alle eindeutigen Klassen für das Dropdown ermitteln raw_classes = set(card.get('Klasse', '').strip() for card in all_cards if card.get('Klasse'))
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) available_classes = sorted(list(raw_classes))
db = client[cfg.MONGODB_DB]
student_cards_collection = db['student_cards']
# Extrahiere alle vorhandenen Klassen, entferne leere/None Werte und sortiere sie
raw_classes = student_cards_collection.distinct('Klasse')
available_classes = sorted([c for c in raw_classes if c])
client.close()
return render_template( return render_template(
'student_cards_admin.html', 'student_cards_admin.html',
student_cards=student_cards, student_cards=all_cards, # Hier all_cards übergeben
edit_mode=edit_mode, edit_mode=edit_mode,
form_data=form_data, form_data=form_data,
available_classes=available_classes, available_classes=available_classes, # Enthält nun die lesbaren Klassen
config=cfg.get_school_info() config=cfg.get_school_info()
) )
@@ -4439,10 +4431,19 @@ def student_card_class_barcode_download():
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
student_cards = db['student_cards'] student_cards = db['student_cards']
class_cards = list(student_cards.find({'Klasse': class_name}).sort('AusweisId', 1)) # 1. Alle Ausweise laden
class_cards = [_decrypt_student_card_doc(card) for card in class_cards] all_cards = list(student_cards.find().sort('AusweisId', 1))
client.close() 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: if not class_cards:
flash(f'Keine Ausweise für die Klasse "{class_name}" gefunden.', 'error') flash(f'Keine Ausweise für die Klasse "{class_name}" gefunden.', 'error')
return redirect(url_for('student_cards_admin')) return redirect(url_for('student_cards_admin'))
+71 -50
View File
@@ -805,71 +805,92 @@
deleteLibraryItem(itemId); deleteLibraryItem(itemId);
} }
// ========================================================================= async function startScanner(callback = null) {
// 3. CORE SCANNER ROUTING ENGINE (QUAGGA2) if (scannerRunning) {
// ========================================================================= console.warn("Scanner is already running. Stopping it first...");
function startScanner(targetCallback) { await stopScanner();
const readerWrap = document.getElementById('scanReaderWrap'); }
activeScannerCallback = callback;
const scannerWrap = document.querySelector('.library-scan-reader-wrap');
const toggleBtn = document.getElementById('toggleScannerBtn'); const toggleBtn = document.getElementById('toggleScannerBtn');
activeScannerCallback = targetCallback; // 1. CRITICAL FIX: Make the container visible BEFORE Quagga initializes
// Quagga cannot calculate video dimensions if display is 'none'
if (scannerWrap) {
scannerWrap.style.display = 'block';
}
if (readerWrap) readerWrap.style.display = 'block'; setScanStatus('Starte Kamera...', 'warn');
setScanStatus('Initializing camera...', 'warn');
Quagga.init({ // 2. Initialize Quagga with a slight delay to allow the DOM to render the block
inputStream: { setTimeout(() => {
name: "Live", Quagga.init({
type: "LiveStream", inputStream: {
target: document.querySelector('#library-scanner-container'), name: "Live",
constraints: { type: "LiveStream",
width: 640, // This MUST match the inner div where the video should appear
height: 480, target: document.querySelector('.library-scan-reader'),
facingMode: "environment" constraints: {
width: 640,
height: 480,
facingMode: "environment" // Prefer back camera on mobile
}
}, },
}, locator: {
decoder: { patchSize: "medium",
readers: [ halfSample: true
"code_128_reader", },
"ean_reader", decoder: {
"code_39_reader", // Keep only the barcode types you actually use to improve performance
"upc_reader", readers: ["code_128_reader", "ean_reader", "code_39_reader"]
"codabar_reader", },
"i2of5_reader" locate: true
] }, function(err) {
} if (err) {
}, function(err) { // This will finally log the actual error (e.g., NotAllowedError) if permissions fail
if (err) { console.error("Quagga initialization failed:", err);
console.error('Scanner start failed:', err); setScanStatus('Kamera-Fehler: ' + (err.name || err), 'error');
if (readerWrap) readerWrap.style.display = 'none';
const detail = (err && (err.message || err.name)) ? ` (${err.message || err.name})` : '';
setScanStatus(`Scanner konnte nicht gestartet werden${detail}`, 'error');
return;
}
Quagga.start(); if (scannerWrap) scannerWrap.style.display = 'none';
scannerRunning = true; scannerRunning = false;
return;
}
if (!targetCallback && toggleBtn) { Quagga.start();
toggleBtn.textContent = 'Scanner stoppen'; scannerRunning = true;
} setScanStatus('Scanner bereit.', 'ok');
setScanStatus('Scanner aktiv. Jetzt Code scannen.', 'warn');
}); if (toggleBtn) {
toggleBtn.textContent = 'Scanner stoppen';
}
});
}, 50);
} }
function stopScanner() { async function stopScanner() {
if (!scannerRunning) return; if (!scannerRunning) return;
const readerWrap = document.getElementById('scanReaderWrap'); try {
const toggleBtn = document.getElementById('toggleScannerBtn'); Quagga.stop();
} catch (e) {
console.warn("Error stopping Quagga (it may not have been running):", e);
}
Quagga.stop();
scannerRunning = false; scannerRunning = false;
activeScannerCallback = null; activeScannerCallback = null;
if (readerWrap) readerWrap.style.display = 'none'; // Hide the container to free up UI space
if (toggleBtn) toggleBtn.textContent = 'Scanner starten'; const scannerWrap = document.querySelector('.library-scan-reader-wrap');
setScanStatus('Scanner gestoppt.', 'warn'); if (scannerWrap) {
scannerWrap.style.display = 'none';
}
const toggleBtn = document.getElementById('toggleScannerBtn');
if (toggleBtn) {
toggleBtn.textContent = 'Kamera Scanner'; // Reset button text
}
} }
async function isStudentCardBarcode(code) { async function isStudentCardBarcode(code) {