Compare commits

...

14 Commits

Author SHA1 Message Date
Aiirondev_dev 5c4ab6f0fc Biblitoheksid is also convertet 2026-11-01 17:08:22 +01:00
Aiirondev_dev 79cf958c3c changes to the DIsplayment of the name ussage 2026-11-01 16:56:01 +01:00
Aiirondev_dev 826db109c3 Changes to the Formating 2026-11-01 16:48:28 +01:00
Aiirondev f9b991d991 Changes to fix the Barcode Search 2026-10-27 10:03:00 +00:00
Aiirondev_dev 363f0df530 changes to the student Name Processing 2026-10-20 20:07:23 +02:00
Aiirondev_dev 34bb3cf4c3 changes to the user processing in the api_item_datail Function 2026-10-20 19:20:36 +02:00
Aiirondev_dev 18706c6d1c slight change to get the right information out of a database record 2026-10-20 19:08:58 +02:00
Aiirondev_dev 9771b98075 logging implementatiion for the user displayment 2026-10-20 18:56:59 +02:00
Aiirondev_dev e380ad6092 Implementation of a nother user decryption detail displayment 2026-10-20 18:44:58 +02:00
Aiirondev_dev d6e8e08b10 Debug logging implementation and changes to the User entcryption 2026-10-20 18:32:23 +02:00
Aiirondev_dev ddb6ab82ad change of the last borrowing user displayment to correctly change the encryption to have the unencryptet name 2026-10-20 17:46:51 +02:00
Aiirondev_dev baefc55257 changes to the get_user_by_student_card function because of a faulty update regarding the searching of the student Cars 2026-10-20 17:29:07 +02:00
Aiirondev_dev 6c359089cd Including of logging information 2026-10-20 17:13:29 +02:00
Aiirondev_dev 479cf3d98c changes to the formating 2026-10-20 16:37:31 +02:00
3 changed files with 136 additions and 60 deletions
+85 -49
View File
@@ -158,6 +158,39 @@ def print(*args, **kwargs):
STUDENT_CARD_ENCRYPTED_FIELDS = ('ausweis_ident', 'SchülerName', 'Klasse', 'Notizen')
def _display_username(user_value):
"""Return a username for a stored user ID, with legacy value support."""
if user_value is None or str(user_value).strip() == '':
return ''
raw_value = str(user_value).strip()
user_doc = us.get_user_by_id(raw_value)
if not user_doc:
user_doc = us.get_user_by_student_card(raw_value)
if user_doc:
return str(user_doc.get('Username') or user_doc.get('username') or raw_value)
return decrypt_text(raw_value)
def _resolve_library_card_id(user_value):
"""Return the card ID for a stored user reference."""
if user_value is None or str(user_value).strip() == '':
return ''
raw_value = str(user_value).strip()
user_doc = us.get_user_by_id(raw_value)
if user_doc:
return str(user_doc.get('StudentCardId') or raw_value).strip().upper()
decrypted_value = decrypt_text(raw_value)
user_doc = us.get_user_by_student_card(decrypted_value)
if user_doc:
return str(user_doc.get('StudentCardId') or decrypted_value).strip().upper()
return str(decrypted_value or raw_value).strip().upper()
def _decrypt_student_card_doc(card_doc):
if not card_doc:
return card_doc
@@ -1212,7 +1245,7 @@ def create_return_reminders():
# Falls der Benutzername verschlüsselt ist, hier entschlüsseln,
# damit der Admin den echten Namen/Ausweis lesen kann.
display_user = decrypt_text(username) if 'decrypt_text' in globals() else username
display_user = _display_username(username)
item_id = str(borrow_doc.get('Item', '')).strip()
item_doc = item_docs.get(item_id, {})
@@ -1383,7 +1416,7 @@ def update_appointment_statuses():
old_status = appointment.get('Status')
activation_user = str(appointment.get('User') or '').strip()
try:
activation_user = decrypt_text(activation_user) if activation_user else ''
activation_user = _display_username(activation_user)
except Exception:
pass
activation_item_name = str(appointment.get('Item') or 'Termin')
@@ -1450,7 +1483,7 @@ def update_appointment_statuses():
holder = ''
if item_doc and item_doc.get('User'):
try:
holder = decrypt_text(str(item_doc['User']))
holder = _display_username(item_doc['User'])
except Exception:
holder = str(item_doc['User'])
if not holder:
@@ -1464,12 +1497,12 @@ def update_appointment_statuses():
)
if active_holder and active_holder.get('User'):
try:
holder = decrypt_text(str(active_holder['User']))
holder = _display_username(active_holder['User'])
except Exception:
holder = str(active_holder['User'])
requested_user = str(appointment.get('User') or '').strip()
try:
requested_user = decrypt_text(requested_user) if requested_user else ''
requested_user = _display_username(requested_user)
except Exception:
pass
item_name = (item_doc or {}).get('Name') or str(item_id)
@@ -1647,8 +1680,7 @@ def update_appointment_statuses():
raw_user = str(appt.get('User', '')).strip()
# Entschlüsselung der AusweisId
decrypted_user = decrypt_text(raw_user) if 'decrypt_text' in globals() else raw_user
target_ausweis_id = decrypted_user if decrypted_user else raw_user
target_ausweis_id = _resolve_library_card_id(raw_user)
item_id = appt.get('Item')
if not target_ausweis_id:
@@ -3702,8 +3734,7 @@ def mahnungen_admin():
item_code = item_doc.get('Code_4', '')
raw_user = str(record.get('User') or '')
decrypted_user = decrypt_text(raw_user)
ausweis_id = (decrypted_user if decrypted_user else raw_user).strip().upper()
ausweis_id = _resolve_library_card_id(raw_user)
student_card = card_map.get(ausweis_id, {})
student_name = student_card.get('SchülerName', ausweis_id if ausweis_id else 'Unbekannt')
@@ -3790,8 +3821,7 @@ def mahnungen_reset(days):
# 3. Schülerausweis ermitteln und entsperren
raw_user = str(record.get('User', ''))
decrypted_user = decrypt_text(raw_user)
ausweis_id = decrypted_user if decrypted_user else raw_user
ausweis_id = _resolve_library_card_id(raw_user)
card = student_cards_col.find_one({'AusweisId': ausweis_id})
if not card and raw_user != ausweis_id:
@@ -3852,8 +3882,7 @@ def mahnungen_send_email():
# Gegenstand und Schülerausweis ermitteln
raw_user = str(record.get('User', ''))
decrypted_user = decrypt_text(raw_user)
ausweis_id = decrypted_user if decrypted_user else raw_user
ausweis_id = _resolve_library_card_id(raw_user)
card = student_cards_col.find_one({'AusweisId': ausweis_id})
student_card = _decrypt_student_card_doc(card) if card and '_decrypt_student_card_doc' in globals() else (
@@ -4205,8 +4234,7 @@ def api_library_return_by_code():
continue
# AusweisId entschlüsseln
decrypted_user = decrypt_text(raw_user) if 'safe_decrypt' in globals() else raw_user
ausweis_id = decrypted_user if decrypted_user else raw_user
ausweis_id = _resolve_library_card_id(raw_user)
# Prüfen, ob der Schüler noch andere eskalierte Ausleihungen hat
# Da die aktuelle Ausleihe oben auf 'completed' gesetzt wurde, wird sie hier nicht mitgezählt!
@@ -4349,10 +4377,14 @@ def api_library_scan_action():
due_date = now + datetime.timedelta(days=borrow_duration_days)
it.update_item_status(item_id, False, borrower_name)
app.logger.info(f"Library scan action: borrowing item {item_id} for student card {student_card_id} until {due_date.isoformat()}")
borrower_user = us.get_user_by_student_card(student_card_id)
app.logger.info(f"Found borrower user: {borrower_user}")
if not borrower_user or not borrower_user.get('_id'):
app.logger.warning(f"No user found for student card {student_card_id}. Cannot record borrowing in ausleihungen.")
return jsonify({'ok': False, 'message': 'Kein Benutzer für diesen Schülerausweis gefunden.'}), 404
it.update_item_status(item_id, False, borrower_name)
au.add_ausleihung(
item_id,
str(borrower_user['_id']),
@@ -4367,7 +4399,7 @@ def api_library_scan_action():
'channel': 'library_scan',
'item_id': item_id,
'item_name': item_doc.get('Name', ''),
'borrower':borrower_name,
'borrower': borrower_name,
'student_card_id': student_card_id,
'borrow_duration_days': borrow_duration_days,
}
@@ -4457,6 +4489,7 @@ def api_item_detail(item_id):
db = client[MONGODB_DB]
ausleihungen_col = db['ausleihungen']
items_col = db['items']
student_cards_col = db['student_cards']
# Determine parent/group id (if this is a child, use its ParentItemId)
parent_id = str(item.get('ParentItemId') or str(item.get('_id')))
@@ -4493,23 +4526,32 @@ def api_item_detail(item_id):
else:
status_label = 'Verfügbar'
# Helper function to get borrower display name
def get_borrower_name(user_raw):
if not user_raw:
return ''
try:
user_doc = student_cards_col.find_one({'_id': ObjectId(user_raw)})
if user_doc:
if user_doc.get('SchülerName'):
return decrypt_text(str(user_doc.get('SchülerName', '')))
return str(user_doc.get('AusweisId', ''))
except Exception:
pass
return str(user_raw)
# Prefer the most recent active borrower across the group
borrower_value = ''
for rec in borrow_records:
if rec.get('Status') == 'active' and rec.get('User'):
try:
user_raw = rec.get('User')
if user_raw is not None:
borrower_value = decrypt_text(str(user_raw))
except Exception:
borrower_value = str(rec.get('User', ''))
borrower_value = get_borrower_name(rec.get('User'))
break
if not borrower_value and item.get('User'):
try:
item_user = item.get('User')
if item_user is not None:
borrower_value = decrypt_text(str(item_user))
borrower_value = _display_username(item_user)
except Exception:
borrower_value = str(item.get('User', ''))
@@ -4525,17 +4567,13 @@ def api_item_detail(item_id):
rows = []
for rec in borrow_records[:3]:
user_raw = rec.get('User')
try:
user = decrypt_text(user_raw) if user_raw is not None else ''
except Exception:
user = user_raw
rec_borrower_name = get_borrower_name(rec.get('User')) or '-'
status = rec.get('Status', '')
start = fmt_dt(rec.get('Start'))
end = fmt_dt(rec.get('End'))
notes = html.escape(str(rec.get('Notes') or ''))
rows.append(
f"<li><strong>{html.escape(str(user or '-'))}</strong> — "
f"<li><strong>{html.escape(rec_borrower_name)}</strong> — "
f"{html.escape(str(status))} — "
f"{html.escape(str(start))} → {html.escape(str(end))}"
f"{(' — ' + notes) if notes else ''}</li>"
@@ -8009,10 +8047,7 @@ def get_planned_bookings(item_id):
bookings = []
for r in cursor:
raw_user = r.get('User') or ''
try:
booking_user = decrypt_text(raw_user) if raw_user else ''
except Exception:
booking_user = str(raw_user)
booking_user = _display_username(raw_user)
bookings.append({
'id': str(r.get('_id')),
'user': booking_user,
@@ -8043,10 +8078,7 @@ def get_planned_bookings_public(item_id):
bookings = []
for r in cursor:
raw_user = r.get('User') or ''
try:
booking_user = decrypt_text(raw_user) if raw_user else ''
except Exception:
booking_user = str(raw_user)
booking_user = _display_username(raw_user)
bookings.append({
'user': booking_user,
'period': r.get('Period'),
@@ -8890,7 +8922,7 @@ def user_del():
fullname = None
users_list.append({
'username': decrypt_text(username),
'username': _display_username(username),
'admin': user.get('Admin', False),
'fullname': fullname,
'name': name,
@@ -9031,7 +9063,8 @@ def admin_borrowings():
has_damage = False
raw_user = r.get('User', '')
decrypted_user = decrypt_text(raw_user) if raw_user else ''
decrypted_user = _display_username(raw_user)
entries.append({
'id': str(r.get('_id')),
@@ -10265,8 +10298,8 @@ def library_item_invoices(item_id):
entries.append({
'borrow_id': str(borrow_doc.get('_id')),
'borrow_status': borrow_doc.get('Status', ''),
'borrow_user': decrypt_text(borrow_doc.get('User', '')) if hasattr(borrow_doc,
'get') and borrow_doc.get(
'borrow_user': _display_username(borrow_doc.get('User', '')) if hasattr(borrow_doc,
'get') and borrow_doc.get(
'User') else '',
'borrow_start': borrow_doc.get('Start').strftime('%d.%m.%Y %H:%M') if isinstance(
borrow_doc.get('Start'), datetime.datetime) else '',
@@ -10366,7 +10399,7 @@ def library_item_invoices(item_id):
))
for b in damage_borrow_docs:
borrow_user = decrypt_text(b.get('User', '')) if b.get('User') else '—'
borrow_user = _display_username(b.get('User', '')) if b.get('User') else '—'
d_data = b.get('DamageData') or {}
if not isinstance(d_data, dict): d_data = {}
@@ -10656,7 +10689,7 @@ def logs():
formatted_items.append({
'Item': item_name,
'User': decrypt_text(username),
'User': _display_username(username),
'Start': start_date,
'End': end_date,
'Duration': duration,
@@ -10698,7 +10731,7 @@ def logs():
note = log_item.get('note', '')
formatted_items.append({
'Item': item_name,
'User': decrypt_text(log_item.get('user', 'Unknown User')),
'User': _display_username(log_item.get('user', 'Unknown User')),
'Start': ts_display,
'End': '-',
'Duration': '-',
@@ -10712,7 +10745,7 @@ def logs():
resolved_count = log_item.get('resolved_count', 0)
formatted_items.append({
'Item': item_name,
'User': decrypt_text(log_item.get('user', 'Unknown User')),
'User': _display_username(log_item.get('user', 'Unknown User')),
'Start': ts_display,
'End': '-',
'Duration': '-',
@@ -11515,6 +11548,7 @@ def my_borrowed_items():
username,
str(current_user.get('_id')) if current_user.get('_id') else '',
str(session.get('user_id') or ''),
str(current_user.get('StudentCardId') or '').strip().upper(),
}
user_ids.discard('')
@@ -11537,6 +11571,7 @@ def my_borrowed_items():
try:
if raw_user:
user_values.add(str(decrypt_text(raw_user)).strip())
user_values.add(_resolve_library_card_id(raw_user))
except Exception:
pass
@@ -11594,6 +11629,7 @@ def my_borrowed_items():
try:
if raw_item_user:
item_user_values.add(str(decrypt_text(raw_item_user)).strip())
item_user_values.add(_resolve_library_card_id(raw_item_user))
except Exception:
pass
@@ -11917,10 +11953,10 @@ def admin_damaged_items():
# Benutzernamen der Ausleihe entschlüsseln
if active_borrow and active_borrow.get('User'):
active_borrow['User'] = decrypt_text(active_borrow['User'])
active_borrow['User'] = _display_username(active_borrow['User'])
# Direkt am Item hinterlegten Nutzer entschlüsseln
item_user = decrypt_text(item.get('User')) if item.get('User') else ''
item_user = _display_username(item.get('User')) if item.get('User') else ''
# Datenstruktur exakt für dein Jinja2 Template aufbauen
damaged_items.append({
@@ -12493,7 +12529,7 @@ def cancel_ausleihung_route(id):
ausleihung_status = ausleihung.get('Status', 'unknown')
raw_ausleihung_user = ausleihung.get('User', '')
try:
ausleihung_user = decrypt_text(raw_ausleihung_user) if raw_ausleihung_user else ''
ausleihung_user = _display_username(raw_ausleihung_user)
except Exception:
ausleihung_user = str(raw_ausleihung_user or '')
print(f"Found ausleihung: ID={id}, Status={ausleihung_status}")
+47 -7
View File
@@ -695,13 +695,17 @@ def get_user_by_student_card(student_card_id):
if not normalized:
return None
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client)
users = db['student_cards']
found_user = users.find_one({'SchülerName': normalized})
client.close()
# Do not call dp.decrypt_text() here because found_user is a MongoDB dictionary.
return found_user
try:
db = _get_tenant_db(client)
users = db['users']
return users.find_one({
'$or': [
{'StudentCardId': normalized},
{'StudentCardId': student_card_id},
]
})
finally:
client.close()
def get_user_by_student_ident(student_ident):
"""Return user dict by student ident by decrypting all cards and matching."""
@@ -874,6 +878,42 @@ def get_user(username):
client.close()
def get_user_by_id(user_id):
"""Retrieve a user by MongoDB document ID."""
if user_id is None:
return None
lookup_values = [str(user_id)]
if ObjectId.is_valid(str(user_id)):
lookup_values.insert(0, ObjectId(str(user_id)))
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
try:
def find_in_db(database_name):
users = client[database_name]['users']
for lookup_value in lookup_values:
user = users.find_one({'_id': lookup_value})
if user:
return user
return None
tenant_db, tenant_id = _resolve_request_tenant_db()
if tenant_db:
user = find_in_db(tenant_db)
if user:
return user
if _has_tenant_configs() and tenant_db is None:
logger.warning(
"Refusing default DB fallback for user ID lookup because tenant configs exist and no tenant was resolved."
)
return None
return find_in_db(cfg.MONGODB_DB)
finally:
client.close()
def check_admin(username):
"""Check if a user has administrator privileges."""
user = get_user(username)
+4 -4
View File
@@ -3368,10 +3368,11 @@ document.addEventListener('DOMContentLoaded', ()=>{
function loadItems(offset = 0, append = false, searchTerm = '', searchScope = 'all') {
// Keep list payload lightweight; full details are fetched on-demand in openItemQuick.
const searchParams = searchTerm
const searchParams = searchTerm && searchScope === 'title_description'
? `&search=${encodeURIComponent(searchTerm)}&search_scope=${encodeURIComponent(searchScope)}`
: '';
return fetch(`{{ url_for('get_items') }}?offset=${offset}&limit=${MAIN_ADMIN_ITEMS_PAGE_SIZE}&light_mode=true${searchParams}`)
const limit = (searchTerm && searchScope === 'all') ? 5000 : MAIN_ADMIN_ITEMS_PAGE_SIZE;
return fetch(`{{ url_for('get_items') }}?offset=${offset}&limit=${limit}&light_mode=true${searchParams}`)
.then(response => response.json())
.then(data => {
const itemsContainer = document.querySelector('#items-container');
@@ -3390,8 +3391,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
if (!append && (!pageItems || pageItems.length === 0)) {
itemsContainer.innerHTML = `
<div class="no-items-message">
<div>Keine Objekte gefunden</div>
<a class="empty-state-cta" href="{{ url_for('upload_admin') }}">Erstellen sie hier ihr erstes Item</a>
<div>Kein Item gefunden</div>
</div>
`;
if (itemsIndicator) {