diff --git a/Web/modules/database/items.py b/Web/modules/database/items.py index 6288ab6..50513ad 100755 --- a/Web/modules/database/items.py +++ b/Web/modules/database/items.py @@ -19,6 +19,7 @@ Collection Structure: - Status fields: Verfuegbar, User (if currently borrowed) """ from bson.objectid import ObjectId +from bson.errors import InvalidId import datetime import Web.modules.database.settings as cfg from Web.modules.database.settings import MongoClient @@ -101,70 +102,42 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi isbn=None, item_type='general', library_category=None, is_library=False): """ Add a new item to the inventory. - - Args: - name (str): Name of the item - ort (str): Location of the item - beschreibung (str): Description of the item - images (list, optional): List of image filenames for the item - filter (str, optional): Primary filter/category for the item - filter2 (str, optional): Secondary filter/category for the item - filter3 (str, optional): Tertiary filter/category for the item - ansch_jahr (int, optional): Year of acquisition - ansch_kost (float, optional): Cost of acquisition - code_4 (str, optional): 4-digit identification code - reservierbar (bool, optional): Whether the item can be reserved in advance - series_group_id (str, optional): Shared group id for same-type batch items - series_count (int, optional): Total items in the created batch - series_position (int, optional): Position inside the batch (1-based) - is_grouped_sub_item (bool, optional): Whether this item is hidden as sub-item - parent_item_id (str, optional): Parent item id if this is a sub-item - isbn (str, optional): ISBN for books or media items - item_type (str, optional): Type of the item (e.g., 'general', 'book', 'cd') - library_category (str, optional): Library category for the item - - Returns: - ObjectId: ID of the new item or None if failed """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] - # Set default values for optional parameters - if images is None: - images = [] + if images is None: + images = [] - item = { - 'Name': name, - 'Ort': ort, - 'Beschreibung': beschreibung, - 'Images': images, - 'Verfuegbar': True, - 'Reservierbar': reservierbar, - 'Filter': filter, - 'Filter2': filter2, - 'Filter3': filter3, - 'Anschaffungsjahr': ansch_jahr, - 'Anschaffungskosten': ansch_kost, - 'Code_4': code_4, - 'ISBN': isbn, - 'library_category': library_category, - 'ItemType': item_type, - 'is_library': is_library, - 'SeriesGroupId': series_group_id, - 'SeriesCount': series_count, - 'SeriesPosition': series_position, - 'IsGroupedSubItem': is_grouped_sub_item, - 'ParentItemId': parent_item_id, - 'Created': datetime.datetime.now(), - 'LastUpdated': datetime.datetime.now() - } - result = items.insert_one(item) - item_id = result.inserted_id - - client.close() - return item_id + item = { + 'Name': name, + 'Ort': ort, + 'Beschreibung': beschreibung, + 'Images': images, + 'Verfuegbar': True, + 'Reservierbar': reservierbar, + 'Filter': filter, + 'Filter2': filter2, + 'Filter3': filter3, + 'Anschaffungsjahr': ansch_jahr, + 'Anschaffungskosten': ansch_kost, + 'Code_4': code_4, + 'ISBN': isbn, + 'library_category': library_category, + 'ItemType': item_type, + 'is_library': is_library, + 'SeriesGroupId': series_group_id, + 'SeriesCount': series_count, + 'SeriesPosition': series_position, + 'IsGroupedSubItem': is_grouped_sub_item, + 'ParentItemId': parent_item_id, + 'Created': datetime.datetime.now(), + 'LastUpdated': datetime.datetime.now() + } + result = items.insert_one(item) + return result.inserted_id except Exception as e: print(f"Error adding item: {e}") return None @@ -173,28 +146,21 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi def remove_item(id): """ Soft-delete an item from the inventory. - - Args: - id (str): ID of the item to remove - - Returns: - bool: True if successful, False otherwise """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - result = items.update_one( - {'_id': ObjectId(id), 'Deleted': {'$ne': True}}, - {'$set': { - 'Deleted': True, - 'DeletedAt': datetime.datetime.now(), - 'LastUpdated': datetime.datetime.now(), - 'Verfuegbar': False, - }} - ) - client.close() - return result.modified_count > 0 + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + result = items.update_one( + {'_id': ObjectId(id), 'Deleted': {'$ne': True}}, + {'$set': { + 'Deleted': True, + 'DeletedAt': datetime.datetime.now(), + 'LastUpdated': datetime.datetime.now(), + 'Verfuegbar': False, + }} + ) + return result.modified_count > 0 except Exception as e: print(f"Error removing item: {e}") return False @@ -203,138 +169,117 @@ def remove_item(id): def get_group_item_ids(id): """ Resolve all item ids that belong to the same grouped series as the given item. - - For non-grouped items, this returns only the provided id. - - Args: - id (str): ID of any item in the group - - Returns: - list[str]: All related item IDs (including the parent item) """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] - base_item = items.find_one(_active_record_query({'_id': ObjectId(id)})) - if not base_item: - client.close() - return [] + base_item = items.find_one(_active_record_query({'_id': ObjectId(id)})) + if not base_item: + return [] - resolved_ids = set() + resolved_ids = set() + series_group_id = base_item.get('SeriesGroupId') - # Prefer SeriesGroupId because it represents the full logical group. - series_group_id = base_item.get('SeriesGroupId') - if series_group_id: - for group_item in items.find(_active_record_query({'SeriesGroupId': series_group_id}), {'_id': 1}): - resolved_ids.add(str(group_item['_id'])) - else: - resolved_ids.add(str(base_item['_id'])) - - parent_item_id = base_item.get('ParentItemId') - if parent_item_id: - resolved_ids.add(str(parent_item_id)) - for sibling in items.find(_active_record_query({'ParentItemId': str(parent_item_id), 'IsGroupedSubItem': True}), {'_id': 1}): - resolved_ids.add(str(sibling['_id'])) + if series_group_id: + for group_item in items.find(_active_record_query({'SeriesGroupId': series_group_id}), {'_id': 1}): + resolved_ids.add(str(group_item['_id'])) else: - for child in items.find(_active_record_query({'ParentItemId': str(base_item['_id']), 'IsGroupedSubItem': True}), {'_id': 1}): - resolved_ids.add(str(child['_id'])) + resolved_ids.add(str(base_item['_id'])) - client.close() - return list(resolved_ids) + parent_item_id = base_item.get('ParentItemId') + if parent_item_id: + resolved_ids.add(str(parent_item_id)) + for sibling in items.find(_active_record_query({'ParentItemId': str(parent_item_id), 'IsGroupedSubItem': True}), {'_id': 1}): + resolved_ids.add(str(sibling['_id'])) + else: + for child in items.find(_active_record_query({'ParentItemId': str(base_item['_id']), 'IsGroupedSubItem': True}), {'_id': 1}): + resolved_ids.add(str(child['_id'])) + + return list(resolved_ids) except Exception as e: print(f"Error resolving group item IDs: {e}") return [] -def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter2, filter3, +def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter2, filter3, ansch_jahr, ansch_kost, code_4, reservierbar, isbn=None, item_type='general'): try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] - old_item = items.find_one({'_id': ObjectId(id)}) - if not old_item: - return False - - series_group_id = old_item.get('SeriesGroupId') + old_item = items.find_one({'_id': ObjectId(id)}) + if not old_item: + return False - shared_update = { - 'Name': name, - 'Ort': ort, - 'Beschreibung': beschreibung, - 'Images': images, - 'Filter': filter1, - 'Filter2': filter2, - 'Filter3': filter3, - 'Anschaffungsjahr': ansch_jahr, - 'Anschaffungskosten': ansch_kost, - 'Reservierbar': reservierbar, - 'ISBN': isbn, - 'ItemType': item_type, - 'Verfuegbar': verfuegbar, - 'LastUpdated': datetime.datetime.now() - } + series_group_id = old_item.get('SeriesGroupId') - specific_update = shared_update.copy() - specific_update['Code_4'] = code_4 + shared_update = { + 'Name': name, + 'Ort': ort, + 'Beschreibung': beschreibung, + 'Images': images, + 'Filter': filter1, + 'Filter2': filter2, + 'Filter3': filter3, + 'Anschaffungsjahr': ansch_jahr, + 'Anschaffungskosten': ansch_kost, + 'Reservierbar': reservierbar, + 'ISBN': isbn, + 'ItemType': item_type, + 'Verfuegbar': verfuegbar, + 'LastUpdated': datetime.datetime.now() + } - items.update_one({'_id': ObjectId(id)}, {'$set': specific_update}) + specific_update = shared_update.copy() + specific_update['Code_4'] = code_4 - if series_group_id: - items.update_many( - { - 'SeriesGroupId': series_group_id, - '_id': {'$ne': ObjectId(id)} - }, - {'$set': shared_update} - ) + items.update_one({'_id': ObjectId(id)}, {'$set': specific_update}) - client.close() - return True + if series_group_id: + items.update_many( + { + 'SeriesGroupId': series_group_id, + '_id': {'$ne': ObjectId(id)} + }, + {'$set': shared_update} + ) + + return True except Exception as e: print(f"Error updating item: {e}") return False + def update_item_status(id, verfuegbar, user=None): """ Update the availability status of an inventory item. - - Args: - id (str): ID of the item to update - verfuegbar (bool): New availability status - user (str, optional): Username of person who borrowed the item - - Returns: - bool: True if successful, False otherwise """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] - update_data = { - 'Verfuegbar': verfuegbar, - 'LastUpdated': datetime.datetime.now() - } + update_data = { + 'Verfuegbar': verfuegbar, + 'LastUpdated': datetime.datetime.now() + } - update_query = {'$set': update_data} + update_query = {'$set': update_data} - if user is not None: - update_data['User'] = dp.encrypt_text(user) - elif verfuegbar: - # If item is being marked as available, clear the user field - update_query['$unset'] = {'User': ""} + if user is not None: + update_data['User'] = dp.encrypt_text(user) + elif verfuegbar: + update_query['$unset'] = {'User': ""} - result = items.update_one( - {'_id': ObjectId(id)}, - update_query - ) + result = items.update_one( + {'_id': ObjectId(id)}, + update_query + ) - client.close() - return result.modified_count > 0 + return result.modified_count > 0 except Exception as e: print(f"Error updating item status: {e}") return False @@ -343,31 +288,23 @@ def update_item_status(id, verfuegbar, user=None): def update_item_exemplare_status(id, exemplare_status): """ Update the exemplar status of an inventory item. - - Args: - id (str): ID of the item to update - exemplare_status (list): List of status objects for each exemplar - - Returns: - bool: True if successful, False otherwise """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] - update_data = { - 'ExemplareStatus': exemplare_status, - 'LastUpdated': datetime.datetime.now() - } + update_data = { + 'ExemplareStatus': exemplare_status, + 'LastUpdated': datetime.datetime.now() + } - result = items.update_one( - {'_id': ObjectId(id)}, - {'$set': update_data} - ) + result = items.update_one( + {'_id': ObjectId(id)}, + {'$set': update_data} + ) - client.close() - return result.modified_count > 0 + return result.modified_count > 0 except Exception as e: print(f"Error updating exemplar status: {e}") return False @@ -376,34 +313,21 @@ def update_item_exemplare_status(id, exemplare_status): def is_code_unique(code_4, exclude_id=None): """ Check if a given code is unique (not used by any other item). - - Args: - code_4 (str): The code to check - exclude_id (str, optional): ID of item to exclude from the check (for edit operations) - - Returns: - bool: True if code is unique, False if already in use """ if not code_4 or code_4.strip() == "": - # Empty codes are not considered unique return False - - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - - # Build query to find items with this code - query = {'Code_4': code_4, 'Deleted': {'$ne': True}} - - # If we're editing an item, exclude it from the uniqueness check - if exclude_id: - query['_id'] = {'$ne': ObjectId(exclude_id)} - - # Check if any items with this code exist - count = items.count_documents(query) - - client.close() - return count == 0 + + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + + query = {'Code_4': code_4, 'Deleted': {'$ne': True}} + + if exclude_id: + query['_id'] = {'$ne': ObjectId(exclude_id)} + + count = items.count_documents(query) + return count == 0 # === ITEM RETRIEVAL === @@ -411,21 +335,19 @@ def is_code_unique(code_4, exclude_id=None): def get_items(): """ Retrieve all inventory items. - - Returns: - list: List of all inventory item documents with string IDs """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - items_return = items.find(_active_record_query(_non_library_query())) - items_list = [] - for item in items_return: - item['_id'] = str(item['_id']) - items_list.append(item) - client.close() - return items_list + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + items_return = items.find(_active_record_query(_non_library_query())) + + items_list = [] + for item in items_return: + item['_id'] = str(item['_id']) + items_list.append(item) + + return items_list except Exception as e: print(f"Error retrieving items: {e}") return [] @@ -434,21 +356,19 @@ def get_items(): def get_available_items(): """ Retrieve all available inventory items. - - Returns: - list: List of available inventory item documents with string IDs """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - items_return = items.find(_active_record_query(_non_library_query({'Verfuegbar': True}))) - items_list = [] - for item in items_return: - item['_id'] = str(item['_id']) - items_list.append(item) - client.close() - return items_list + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + items_return = items.find(_active_record_query(_non_library_query({'Verfuegbar': True}))) + + items_list = [] + for item in items_return: + item['_id'] = str(item['_id']) + items_list.append(item) + + return items_list except Exception as e: print(f"Error retrieving available items: {e}") return [] @@ -457,25 +377,24 @@ def get_available_items(): def get_borrowed_items(): """ Retrieve all currently borrowed inventory items. - - Returns: - list: List of borrowed inventory item documents with string IDs """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - items_return = items.find(_active_record_query(_non_library_query({'Verfuegbar': False}))) - items_list = [] - for item in items_return: - item['_id'] = str(item['_id']) - items_list.append(item) - client.close() - return items_list + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + items_return = items.find(_active_record_query(_non_library_query({'Verfuegbar': False}))) + + items_list = [] + for item in items_return: + item['_id'] = str(item['_id']) + items_list.append(item) + + return items_list except Exception as e: print(f"Error retrieving borrowed items: {e}") return [] + def get_item(id, decrypt=True): """ Retrieve an inventory item by ID, with optional decryption. @@ -485,37 +404,32 @@ def get_item(id, decrypt=True): return None try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - query = _active_record_query({'_id': item_id}) - item = items.find_one(query) - if item: - item['_id'] = str(item['_id']) - if decrypt: - decrypt_item_user_data(item) - return item + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + query = _active_record_query({'_id': item_id}) + item = items.find_one(query) + + if item: + item['_id'] = str(item['_id']) + if decrypt: + decrypt_item_user_data(item) + return item except Exception as e: print(f"Error retrieving item {id}: {e}") return None + def get_item_by_name(name): """ Retrieve a specific inventory item by its name. - - Args: - name (str): Name of the item to retrieve - - Returns: - dict: The inventory item document or None if not found """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - item = items.find_one(_active_record_query({'Name': name})) - client.close() - return item + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + item = items.find_one(_active_record_query(_non_library_query({'Name': name}))) + return item except Exception as e: print(f"Error retrieving item by name: {e}") return None @@ -524,35 +438,25 @@ def get_item_by_name(name): def get_items_by_filter(filter_value): """ Retrieve inventory items matching a specific filter/category. - - Args: - filter_value (str): Filter value to search for - - Returns: - list: List of items matching the filter in primary, secondary, or tertiary category """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - - # Use $or to find matches in any filter field - query = _active_record_query(_non_library_query({ - '$or': [ - {'Filter': filter_value}, - {'Filter2': filter_value}, - {'Filter3': filter_value} - ] - })) - - results = list(items.find(query)) - client.close() - - # Convert ObjectId to string - for item in results: - item['_id'] = str(item['_id']) - - return results + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + + query = _active_record_query(_non_library_query({ + '$or': [ + {'Filter': filter_value}, + {'Filter2': filter_value}, + {'Filter3': filter_value} + ] + })) + + results = list(items.find(query)) + for item in results: + item['_id'] = str(item['_id']) + + return results except Exception as e: print(f"Error retrieving items by filter: {e}") return [] @@ -561,96 +465,70 @@ def get_items_by_filter(filter_value): def get_filters(): """ Retrieve all unique filter/category values from the inventory. - - Returns: - list: Combined list of all primary, secondary and tertiary filter values """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - non_library = _active_record_query(_non_library_query()) - filters = items.distinct('Filter', non_library) - filters2 = items.distinct('Filter2', non_library) - filters3 = items.distinct('Filter3', non_library) - - # Combine filters and remove None/empty values - all_filters = [f for f in filters + filters2 + filters3 if f] - - # Remove duplicates while preserving order - unique_filters = [] - for f in all_filters: - if f not in unique_filters: - unique_filters.append(f) - - client.close() - return unique_filters + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + non_library = _active_record_query(_non_library_query()) + + filters = items.distinct('Filter', non_library) + filters2 = items.distinct('Filter2', non_library) + filters3 = items.distinct('Filter3', non_library) + + all_filters = [f for f in filters + filters2 + filters3 if f] + + unique_filters = [] + for f in all_filters: + if f not in unique_filters: + unique_filters.append(f) + + return unique_filters except Exception as e: print(f"Error retrieving filters: {e}") return [] def get_primary_filters(): - """ - Retrieve all unique primary filter values. - - Returns: - list: List of all primary filter values - """ + """Retrieve all unique primary filter values.""" try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - filters = [f for f in items.distinct('Filter', _active_record_query(_non_library_query())) if f] - client.close() - - # Add predefined values - predefined = get_predefined_filter_values(1) - return sorted(list(set(filters + predefined))) + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + filters = [f for f in items.distinct('Filter', _active_record_query(_non_library_query())) if f] + + predefined = get_predefined_filter_values(1) + return sorted(list(set(filters + predefined))) except Exception as e: print(f"Error retrieving primary filters: {e}") return [] def get_secondary_filters(): - """ - Retrieve all unique secondary filter values. - - Returns: - list: List of all secondary filter values - """ + """Retrieve all unique secondary filter values.""" try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - filters = [f for f in items.distinct('Filter2', _active_record_query(_non_library_query())) if f] - client.close() - - # Add predefined values - predefined = get_predefined_filter_values(2) - return sorted(list(set(filters + predefined))) + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + filters = [f for f in items.distinct('Filter2', _active_record_query(_non_library_query())) if f] + + predefined = get_predefined_filter_values(2) + return sorted(list(set(filters + predefined))) except Exception as e: print(f"Error retrieving secondary filters: {e}") return [] def get_tertiary_filters(): - """ - Retrieve all unique tertiary filter values. - - Returns: - list: List of all tertiary filter values - """ + """Retrieve all unique tertiary filter values.""" try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - filters = [f for f in items.distinct('Filter3', _active_record_query(_non_library_query())) if f] - client.close() - - # Add predefined values - predefined = get_predefined_filter_values(3) - return sorted(list(set(filters + predefined))) + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + filters = [f for f in items.distinct('Filter3', _active_record_query(_non_library_query())) if f] + + predefined = get_predefined_filter_values(3) + return sorted(list(set(filters + predefined))) except Exception as e: print(f"Error retrieving tertiary filters: {e}") return [] @@ -659,25 +537,17 @@ def get_tertiary_filters(): def get_item_by_code_4(code_4): """ Retrieve inventory items matching a specific 4-digit code. - - Args: - code_4 (str): 4-digit code to search for - - Returns: - list: List of items matching the code """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - results = list(items.find(_active_record_query(_non_library_query({"Code_4": code_4})))) - - # Convert ObjectId to string - for item in results: - item['_id'] = str(item['_id']) - - client.close() - return results + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + results = list(items.find(_active_record_query(_non_library_query({"Code_4": code_4})))) + + for item in results: + item['_id'] = str(item['_id']) + + return results except Exception as e: print(f"Error retrieving item by code: {e}") return [] @@ -688,42 +558,33 @@ def get_item_by_code_4(code_4): def unstuck_item(id): """ Remove all borrowing records for a specific item to reset its status. - Used to fix problematic or stuck items. - - Args: - id (str): ID of the item to unstick - - Returns: - bool: True if successful, False otherwise """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - ausleihungen = db['ausleihungen'] - result = ausleihungen.update_many( - {'Item': id, 'Status': {'$nin': ['cancelled', 'deleted']}}, - {'$set': { - 'Status': 'cancelled', - 'CancelledReason': 'unstuck_reset', - 'LastUpdated': datetime.datetime.now() - }} - ) - - # Also reset the item status - items = db['items'] - items.update_one( - {'_id': ObjectId(id)}, - { - '$set': { - 'Verfuegbar': True, + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + ausleihungen = db['ausleihungen'] + + ausleihungen.update_many( + {'Item': id, 'Status': {'$nin': ['cancelled', 'deleted']}}, + {'$set': { + 'Status': 'cancelled', + 'CancelledReason': 'unstuck_reset', 'LastUpdated': datetime.datetime.now() - }, - '$unset': {'User': ""} - } - ) - - client.close() - return True + }} + ) + + items = db['items'] + items.update_one( + {'_id': ObjectId(id)}, + { + '$set': { + 'Verfuegbar': True, + 'LastUpdated': datetime.datetime.now() + }, + '$unset': {'User': ""} + } + ) + return True except Exception as e: print(f"Error unsticking item: {e}") return False @@ -732,173 +593,125 @@ def unstuck_item(id): def get_predefined_filter_values(filter_num): """ Get predefined values for a specific filter. - - Args: - filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) - - Returns: - list: List of predefined filter values """ - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - - # Use a dedicated collection for filter presets - filter_presets = db['filter_presets'] - - # Find the document for the specified filter - filter_doc = filter_presets.find_one({'filter_num': filter_num}) - - client.close() - - if filter_doc and 'values' in filter_doc: - # Sort values alphabetically - return sorted(filter_doc['values']) - else: - # Create empty document if it doesn't exist - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: db = client[cfg.MONGODB_DB] filter_presets = db['filter_presets'] - filter_presets.update_one( - {'filter_num': filter_num}, - {'$set': {'values': []}}, - upsert=True - ) - client.close() - return [] + + filter_doc = filter_presets.find_one({'filter_num': filter_num}) + + if filter_doc and 'values' in filter_doc: + return sorted(filter_doc['values']) + else: + filter_presets.update_one( + {'filter_num': filter_num}, + {'$set': {'values': []}}, + upsert=True + ) + return [] + def add_predefined_filter_value(filter_num, value): """ Add a new predefined value to a filter. - - Args: - filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) - value (str): Value to add - - Returns: - bool: True if value was added, False if it already existed """ - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - filter_presets = db['filter_presets'] - - # Check if value already exists - filter_doc = filter_presets.find_one({ - 'filter_num': filter_num, - 'values': value - }) - - if filter_doc: - # Value already exists - client.close() - return False - - # Add the value to the filter - result = filter_presets.update_one( - {'filter_num': filter_num}, - {'$push': {'values': value}}, - upsert=True - ) - - client.close() - return result.modified_count > 0 or result.upserted_id is not None + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + filter_presets = db['filter_presets'] + + filter_doc = filter_presets.find_one({ + 'filter_num': filter_num, + 'values': value + }) + + if filter_doc: + return False + + result = filter_presets.update_one( + {'filter_num': filter_num}, + {'$push': {'values': value}}, + upsert=True + ) + return result.modified_count > 0 or result.upserted_id is not None + def remove_predefined_filter_value(filter_num, value): """ Remove a predefined value from a filter. - - Args: - filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) - value (str): Value to remove - - Returns: - bool: True if value was removed, False otherwise """ - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - filter_presets = db['filter_presets'] - - # Remove the value from the filter - result = filter_presets.update_one( - {'filter_num': filter_num}, - {'$pull': {'values': value}} - ) - - client.close() - return result.modified_count > 0 + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + filter_presets = db['filter_presets'] + + result = filter_presets.update_one( + {'filter_num': filter_num}, + {'$pull': {'values': value}} + ) + return result.modified_count > 0 def edit_predefined_filter_value(filter_num, old_value, new_value): """ Edit a predefined value from a filter and update all matching items. - - Args: - filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) - old_value (str): Value to replace - new_value (str): New value - - Returns: - bool: True if value was updated, False otherwise """ - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - filter_presets = db['filter_presets'] - - # Check if the new value already exists - existing = filter_presets.find_one({ - 'filter_num': filter_num, - 'values': new_value - }) - - if existing and old_value != new_value: - client.close() - return False - - # Update the value in the filter - result = filter_presets.update_one( - {'filter_num': filter_num, 'values': old_value}, - {'$set': {'values.$': new_value}} - ) - - if result.modified_count > 0: - items = db['items'] - filter_field = 'Filter' if filter_num == 1 else f'Filter{filter_num}' - - # Also update all items that use this filter - items.update_many( - {filter_field: old_value}, - {'$set': {filter_field: new_value}} + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + filter_presets = db['filter_presets'] + + existing = filter_presets.find_one({ + 'filter_num': filter_num, + 'values': new_value + }) + + if existing and old_value != new_value: + return False + + result = filter_presets.update_one( + {'filter_num': filter_num, 'values': old_value}, + {'$set': {'values.$': new_value}} ) - - client.close() - return result.modified_count > 0 + + if result.modified_count > 0: + items = db['items'] + filter_field = 'Filter' if filter_num == 1 else f'Filter{filter_num}' + + items.update_many( + {filter_field: old_value}, + {'$set': {filter_field: new_value}} + ) + + return result.modified_count > 0 + def get_filter_names(): """Get customized filter category names.""" - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - names_doc = db.settings.find_one({'setting_type': 'filter_names'}) - client.close() - if names_doc and 'names' in names_doc: - return names_doc['names'] - return { - '1': 'Fach/Kategorie', - '2': 'System/Bereich', - '3': 'Typ/Art' - } + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + names_doc = db.settings.find_one({'setting_type': 'filter_names'}) + + if names_doc and 'names' in names_doc: + return names_doc['names'] + + return { + '1': 'Fach/Kategorie', + '2': 'System/Bereich', + '3': 'Typ/Art' + } + def set_filter_name(filter_num, name): """Set custom name for a filter category.""" - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - names = get_filter_names() - names[str(filter_num)] = name - db.settings.update_one( - {'setting_type': 'filter_names'}, - {'$set': {'names': names}}, - upsert=True - ) - client.close() - return True + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + names = get_filter_names() + names[str(filter_num)] = name + + db.settings.update_one( + {'setting_type': 'filter_names'}, + {'$set': {'names': names}}, + upsert=True + ) + return True # === LOCATION MANAGEMENT === @@ -906,35 +719,27 @@ def set_filter_name(filter_num, name): def get_predefined_locations(): """ Get list of all predefined locations/placement options. - - Returns: - list: List of predefined location strings """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - - # Check if settings collection exists, create if not - if 'settings' not in db.list_collection_names(): - db.create_collection('settings') - - # Get settings document or create if it doesn't exist - settings_collection = db['settings'] - location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'}) - - if not location_settings: - # Create default settings document if it doesn't exist - settings_collection.insert_one({ - 'setting_type': 'predefined_locations', - 'locations': [] - }) - return [] - - # Return the predefined locations - locations = location_settings.get('locations', []) - client.close() - return sorted(locations) - + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + + if 'settings' not in db.list_collection_names(): + db.create_collection('settings') + + settings_collection = db['settings'] + location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'}) + + if not location_settings: + settings_collection.insert_one({ + 'setting_type': 'predefined_locations', + 'locations': [] + }) + return [] + + locations = location_settings.get('locations', []) + return sorted(locations) + except Exception as e: print(f"Error getting predefined locations: {str(e)}") return [] @@ -943,52 +748,38 @@ def get_predefined_locations(): def add_predefined_location(location): """ Add a new predefined location. - - Args: - location (str): Location to add - - Returns: - bool: True if added successfully, False if already exists """ if not location or not isinstance(location, str): return False - + location = location.strip() if not location: return False - + try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - settings_collection = db['settings'] - - # Check if settings document exists, create if not - location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'}) - - if not location_settings: - # Create with the new location - settings_collection.insert_one({ - 'setting_type': 'predefined_locations', - 'locations': [location] - }) - client.close() + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + settings_collection = db['settings'] + + location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'}) + + if not location_settings: + settings_collection.insert_one({ + 'setting_type': 'predefined_locations', + 'locations': [location] + }) + return True + + current_locations = location_settings.get('locations', []) + if any(loc.lower() == location.lower() for loc in current_locations): + return False + + settings_collection.update_one( + {'setting_type': 'predefined_locations'}, + {'$push': {'locations': location}} + ) return True - - # Check if location already exists (case-insensitive) - current_locations = location_settings.get('locations', []) - if any(loc.lower() == location.lower() for loc in current_locations): - client.close() - return False - - # Add the new location - settings_collection.update_one( - {'setting_type': 'predefined_locations'}, - {'$push': {'locations': location}} - ) - - client.close() - return True - + except Exception as e: print(f"Error adding predefined location: {str(e)}") return False @@ -997,29 +788,21 @@ def add_predefined_location(location): def remove_predefined_location(location): """ Remove a predefined location. - - Args: - location (str): Location to remove - - Returns: - bool: True if removed successfully """ if not location: return False - + try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - settings_collection = db['settings'] - - result = settings_collection.update_one( - {'setting_type': 'predefined_locations'}, - {'$pull': {'locations': location}} - ) - - client.close() - return result.modified_count > 0 - + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + settings_collection = db['settings'] + + result = settings_collection.update_one( + {'setting_type': 'predefined_locations'}, + {'$pull': {'locations': location}} + ) + return result.modified_count > 0 + except Exception as e: print(f"Error removing predefined location: {str(e)}") return False @@ -1028,129 +811,36 @@ def remove_predefined_location(location): def update_item_next_appointment(item_id, appointment_data): """ Update an item with information about its next scheduled appointment. - - Args: - item_id (str): ID of the item to update - appointment_data (dict): Appointment information containing: - - date: Date of the appointment - - start_period: Start period number - - end_period: End period number - - user: Username who scheduled the appointment - - notes: Optional notes - - appointment_id: ID of the appointment booking - - Returns: - bool: True if successful, False otherwise """ try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - - # Format the appointment data for storage - # Ensure date is a datetime object for MongoDB storage - appointment_date = appointment_data['date'] - if isinstance(appointment_date, datetime.date) and not isinstance(appointment_date, datetime.datetime): - # Convert date to datetime for MongoDB compatibility - appointment_date = datetime.datetime.combine(appointment_date, datetime.time()) - - next_appointment = { - 'date': appointment_date, - 'end_date': appointment_data.get('end_date', appointment_date), - 'start_period': appointment_data['start_period'], - 'end_period': appointment_data['end_period'], - 'user': dp.encrypt_text(appointment_data['user']), - 'notes': appointment_data.get('notes', ''), - 'appointment_id': appointment_data['appointment_id'], - 'scheduled_at': datetime.datetime.now() - } - - update_data = { - 'NextAppointment': next_appointment, - 'LastUpdated': datetime.datetime.now() - } - - result = items.update_one( - {'_id': ObjectId(item_id)}, - {'$set': update_data} - ) - - client.close() - return result.modified_count > 0 + with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client: + db = client[cfg.MONGODB_DB] + items = db['items'] + + if appointment_data is None: + update_query = { + '$unset': {'NextAppointment': ""}, + '$set': {'LastUpdated': datetime.datetime.now()} + } + else: + data_to_save = appointment_data.copy() + + if 'user' in data_to_save and data_to_save['user']: + data_to_save['user'] = dp.encrypt_text(data_to_save['user']) + + update_query = { + '$set': { + 'NextAppointment': data_to_save, + 'LastUpdated': datetime.datetime.now() + } + } + + result = items.update_one( + {'_id': ObjectId(item_id)}, + update_query + ) + + return result.modified_count > 0 except Exception as e: print(f"Error updating item next appointment: {e}") - return False - - -def clear_item_next_appointment(item_id): - """ - Clear the next appointment information from an item. - - Args: - item_id (str): ID of the item to update - - Returns: - bool: True if successful, False otherwise - """ - try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - - result = items.update_one( - {'_id': ObjectId(item_id)}, - {'$unset': {'NextAppointment': ""}, '$set': {'LastUpdated': datetime.datetime.now()}} - ) - - client.close() - return result.modified_count > 0 - except Exception as e: - print(f"Error clearing item next appointment: {e}") - return False - - -def get_items_with_appointments(): - """ - Retrieve all items that have scheduled appointments. - - Returns: - list: List of items with NextAppointment field - """ - try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - - items_return = items.find({'NextAppointment': {'$exists': True}, 'Deleted': {'$ne': True}}) - items_list = [] - for item in items_return: - item['_id'] = str(item['_id']) - items_list.append(item) - client.close() - return items_list - except Exception as e: - print(f"Error retrieving items with appointments: {e}") - return [] - -def get_current_status(item_id, decrypt=True): - """ - Retrieve the current status of an item, decrypting the user field if present. - """ - oid = _to_object_id(item_id) - if not oid: - return None - - try: - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = client[cfg.MONGODB_DB] - items = db['items'] - item = items.find_one({'_id': oid}, {'Verfuegbar': 1, 'User': 1}) - if item: - item['_id'] = str(item['_id']) - if decrypt: - decrypt_item_user_data(item) - return item - return None - except Exception as e: - print(f"Error retrieving current status for item {item_id}: {e}") - return None \ No newline at end of file + return False \ No newline at end of file diff --git a/Web/modules/database/user.py b/Web/modules/database/user.py index ad857e0..35f5b8e 100755 --- a/Web/modules/database/user.py +++ b/Web/modules/database/user.py @@ -110,13 +110,6 @@ def build_username_from_name(first_name, last_name=''): """ Build a deterministic username abbreviation from first and last name. Uses 3 letters from each name and stores it lowercase. - - Args: - first_name (str): First name - last_name (str): Last name (optional) - - Returns: - str: Generated username """ alias = build_name_synonym(first_name, last_name) return alias.lower() @@ -325,7 +318,6 @@ def get_effective_permissions(username): return build_default_permission_payload('full_access') preset_key = user.get('PermissionPreset') - print(preset_key) payload = build_default_permission_payload(preset_key) payload['actions'] = _normalize_bool_map(user.get('ActionPermissions', {}), payload['actions']) payload['pages'] = _normalize_bool_map(user.get('PagePermissions', {}), payload['pages']) @@ -353,10 +345,10 @@ def update_user_permissions(username, preset_key, action_permissions=None, page_ client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - result = users.update_one({'Username': username}, {'$set': update_data}) + result = users.update_one({'Username': dp.encrypt_text(username)}, {'$set': update_data}) if result.matched_count == 0: - result = users.update_one({'username': username}, {'$set': update_data}) + result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': update_data}) client.close() return result.matched_count > 0 @@ -368,7 +360,7 @@ def get_favorites(username): client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - user = users.find_one({'Username': username}) or users.find_one({'username': username}) + user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)}) client.close() if not user: return [] @@ -383,7 +375,7 @@ def add_favorite(username, item_id): db = _get_tenant_db(client) users = db['users'] users.update_one( - {'$or': [{'Username': username}, {'username': username}]}, + {'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]}, {'$addToSet': {'favorites': ObjectId(item_id)}} ) client.close() @@ -398,7 +390,7 @@ def remove_favorite(username, item_id): db = _get_tenant_db(client) users = db['users'] users.update_one( - {'$or': [{'Username': username}, {'username': username}]}, + {'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]}, {'$pull': {'favorites': ObjectId(item_id)}} ) client.close() @@ -407,16 +399,9 @@ def remove_favorite(username, item_id): return False - def check_password_strength(password): """ Check if a password meets minimum security requirements. - - Args: - password (str): Password to check - - Returns: - bool: True if password is strong enough, False otherwise """ if password is None: return False @@ -436,19 +421,15 @@ def check_password_strength(password): def hashing(password, salt=None): """ - Hasht ein Passwort mit scrypt. - - Wenn kein Salt übergeben wird, wird ein sicherer, zufälliger Salt generiert (für neue Passwörter). - - Format für neue Hashes: v1$$ + Hasht ein Passwort mit scrypt. """ - password_bytes = password.encode('utf-8') # Explizit UTF-8 für Plattformunabhängigkeit - + password_bytes = password.encode('utf-8') + if salt is None: - # Neuer Benutzer / Passwortänderung -> Dynamischer Salt random_salt = os.urandom(16) hashed = hashlib.scrypt(password_bytes, salt=random_salt, n=16384, r=8, p=1) return f"v1${random_salt.hex()}${hashed.hex()}" else: - # Bestehender Benutzer (wird zur Verifizierung aufgerufen) hashed = hashlib.scrypt(password_bytes, salt=salt, n=16384, r=8, p=1) return hashed.hex() @@ -456,25 +437,20 @@ def hashing(password, salt=None): def verify_password(provided_password, stored_password_string): """ Verifiziert ein Passwort gegen einen gespeicherten Hash-String. - Unterstützt das alte Format (statischer Salt) und das neue Format (v1$...). """ if not stored_password_string: return False - # Überprüfung für das neue, sichere Format if stored_password_string.startswith("v1$"): try: _, salt_hex, hash_hex = stored_password_string.split("$") salt_bytes = bytes.fromhex(salt_hex) - # Berechne den Hash des eingegebenen Passworts mit dem extrahierten Salt calculated_hash = hashing(provided_password, salt=salt_bytes) - # Timing-Attack-sicherer Vergleich return hmac.compare_digest(calculated_hash, hash_hex) except (ValueError, TypeError): logger.error("Ungültiges Hash-Format in der Datenbank entdeckt.") return False else: - # Abwärtskompatibilität: Altes Format mit statischem Salt b'some_salt' old_static_salt = b'some_salt' calculated_hash = hashing(provided_password, salt=old_static_salt) return hmac.compare_digest(calculated_hash, stored_password_string) @@ -495,22 +471,21 @@ def check_nm_pwd(username, password): try: db = client[db_name] users = db['users'] - - query = {'$or': [{'Username': username}, {'username': username}]} + + query = {'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]} user_record = users.find_one(query) if user_record is None: - logger.warning("Kein Benutzer für %r in DB %r gefunden.", username, db_name) + logger.warning("Kein Benutzer für %r in DB %r gefunden.", dp.encrypt_text(username), db_name) return None stored_password = user_record.get('Password') or user_record.get('password') - + if not verify_password(password, stored_password): - logger.warning("Falsches Passwort für Benutzer %r in DB %r.", username, db_name) + logger.warning("Falsches Passwort für Benutzer %r in DB %r.", dp.encrypt_text(username), db_name) return None - # Automatische Migration alter Hashes auf das neue Format - if not stored_password.startswith("v1$"): + if stored_password and not stored_password.startswith("v1$"): users.update_one({'_id': user_record['_id']}, {'$set': {'Password': hashing(password)}}) return user_record @@ -532,40 +507,35 @@ def add_user( ): """ Add a new user to the database. - - Args: - username (str): Username for the new user - password (str): Password for the new user - - Returns: - bool: True if user was added successfully, False if password was too weak """ + if not check_password_strength(password): + return False + client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) try: db = _get_tenant_db(client) users = db['users'] - if not check_password_strength(password): - return False + permission_defaults = build_default_permission_payload(permission_preset) + if isinstance(action_permissions, dict): for key, value in action_permissions.items(): permission_defaults['actions'][str(key)] = bool(value) + if isinstance(page_permissions, dict): for key, value in page_permissions.items(): permission_defaults['pages'][str(key)] = bool(value) - if permission_preset == "full_access": - can_admin_preset_based = True - else: - can_admin_preset_based = False - + safe_name = name.strip() if name else '' + safe_last_name = last_name.strip() if last_name else '' + user_doc = { - 'Username': username, + 'Username': dp.encrypt_text(username), 'Password': hashing(password), - 'Admin': can_admin_preset_based, + 'Admin': (permission_preset == "full_access"), 'active_ausleihung': None, - 'name': name.strip() if name else '', - 'last_name': last_name.strip() if last_name else '', + 'name': dp.encrypt_text(safe_name) if safe_name else '', + 'last_name': dp.encrypt_text(safe_last_name) if safe_last_name else '', 'IsStudent': bool(is_student), 'PermissionPreset': permission_defaults['preset'], 'ActionPermissions': permission_defaults['actions'], @@ -602,7 +572,7 @@ def student_card_exists(student_card_id): def get_user_by_student_card(student_card_id): - """Return user by student card id or None.""" + """Return user dict by student card id or None.""" normalized = normalize_student_card_id(student_card_id) if not normalized: return None @@ -611,65 +581,44 @@ def get_user_by_student_card(student_card_id): 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 def make_admin(username): - """ - Grant administrator privileges to a user. - - Args: - username (str): Username of the user to promote - - Returns: - bool: True if user was promoted successfully - """ + """Grant administrator privileges to a user.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - result = users.update_one({'Username': username}, {'$set': {'Admin': True}}) + result = users.update_one({'Username': dp.encrypt_text(username)}, {'$set': {'Admin': True}}) if result.matched_count == 0: - result = users.update_one({'username': username}, {'$set': {'Admin': True}}) + result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': {'Admin': True}}) client.close() return result.matched_count > 0 + def remove_admin(username): - """ - Remove administrator privileges from a user. - - Args: - username (str): Username of the user to demote - - Returns: - bool: True if user was demoted successfully - """ + """Remove administrator privileges from a user.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - result = users.update_one({'Username': username}, {'$set': {'Admin': False}}) + result = users.update_one({'Username': dp.encrypt_text(username)}, {'$set': {'Admin': False}}) if result.matched_count == 0: - result = users.update_one({'username': username}, {'$set': {'Admin': False}}) + result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': {'Admin': False}}) client.close() return result.matched_count > 0 + def get_user(username): - """ - Retrieve a specific user by username. - - Args: - username (str): Username to search for - - Returns: - dict: User document or None if not found - """ + """Retrieve a specific user by username.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) try: def find_in_db(database_name): db = client[database_name] users = db['users'] - return users.find_one({'Username': username}) or users.find_one({'username': username}) + return users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)}) - # Try current tenant first when available tenant_db, tenant_id = _resolve_request_tenant_db() if tenant_db: user = find_in_db(tenant_db) @@ -682,7 +631,6 @@ def get_user(username): ) return None - # Fallback to default configured database user = find_in_db(cfg.MONGODB_DB) if user: return user @@ -693,147 +641,89 @@ def get_user(username): def check_admin(username): - """ - Check if a user has administrator privileges. - - Args: - username (str): Username to check - - Returns: - bool: True if user is an administrator, False otherwise - """ + """Check if a user has administrator privileges.""" user = get_user(username) return bool(user and user.get('Admin', False)) def update_active_ausleihung(username, id_item, ausleihung): - """ - Update a user's active borrowing record. - - Args: - username (str): Username of the user - id_item (str): ID of the borrowed item - ausleihung (str): ID of the borrowing record - - Returns: - bool: True if successful - """ + """Update a user's active borrowing record.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - users.update_one({'Username': username}, {'$set': {'active_ausleihung': {'Item': id_item, 'Ausleihung': ausleihung}}}) + + result = users.update_one( + {'Username': dp.encrypt_text(username)}, + {'$set': {'active_ausleihung': {'Item': id_item, 'Ausleihung': ausleihung}}} + ) + if result.matched_count == 0: + users.update_one( + {'username': dp.encrypt_text(username)}, + {'$set': {'active_ausleihung': {'Item': id_item, 'Ausleihung': ausleihung}}} + ) client.close() return True def get_active_ausleihung(username): - """ - Get a user's active borrowing record. - - Args: - username (str): Username of the user - - Returns: - dict: Active borrowing information or None - """ + """Get a user's active borrowing record.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - user = users.find_one({'Username': username}) - return user['active_ausleihung'] + + user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)}) + client.close() + + if not user: + return None + return user.get('active_ausleihung') def has_active_borrowing(username): - """ - Check if a user currently has an active borrowing. - - Args: - username (str): Username to check - - Returns: - bool: True if user has an active borrowing, False otherwise - """ + """Check if a user currently has an active borrowing.""" try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - - user = users.find_one({'username': username}) - if not user: - user = users.find_one({'Username': username}) - - if not user: - client.close() - return False - - has_active = user.get('active_borrowing', False) - + + user = users.find_one({'username': dp.encrypt_text(username)}) or users.find_one({'Username': dp.encrypt_text(username)}) client.close() - return has_active + + if not user: + return False + + return user.get('active_borrowing', False) except Exception as e: return False def delete_user(username): - """ - Delete a user from the database. - Administrative function for removing user accounts. - - Args: - username (str): Username of the account to delete - - Returns: - bool: True if user was deleted successfully, False otherwise - """ + """Delete a user from the database.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - result = users.delete_one({'username': username}) - client.close() + + result = users.delete_one({'username': dp.encrypt_text(username)}) if result.deleted_count == 0: - # Try with different field name - client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) - db = _get_tenant_db(client) - users = db['users'] - result = users.delete_one({'Username': username}) - client.close() - + result = users.delete_one({'Username': dp.encrypt_text(username)}) + + client.close() return result.deleted_count > 0 def update_active_borrowing(username, item_id, status): - """ - Update a user's active borrowing status. - - Args: - username (str): Username of the user - item_id (str): ID of the borrowed item or None if returning - status (bool): True if borrowing, False if returning - - Returns: - bool: True if successful, False on error - """ + """Update a user's active borrowing status.""" try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - result = users.update_one( - {'username': username}, - {'$set': { - 'active_borrowing': status, - 'borrowed_item': item_id if status else None - }} - ) - + + update_data = {'$set': {'active_borrowing': status, 'borrowed_item': item_id if status else None}} + + result = users.update_one({'username': dp.encrypt_text(username)}, update_data) if result.matched_count == 0: - result = users.update_one( - {'Username': username}, - {'$set': { - 'active_borrowing': status, - 'borrowed_item': item_id if status else None - }} - ) - + result = users.update_one({'Username': dp.encrypt_text(username)}, update_data) + client.close() return result.modified_count > 0 except Exception as e: @@ -841,43 +731,37 @@ def update_active_borrowing(username, item_id, status): def get_name(username): - """ - Retrieve the name that is assosiated with the username. - - Returns: - str: String of name - """ + """Retrieve the name that is associated with the username.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - user = users.find_one({'Username': username}) - name = user.get("name") - return name + + user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)}) + client.close() + + if not user or not user.get("name"): + return "" + + return dp.decrypt_text(user.get("name")) def get_last_name(username): - """ - Retrieve the last_name that is assosiated with the username. - - Returns: - str: String of last_name - """ + """Retrieve the last_name that is associated with the username.""" client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - user = users.find_one({'Username': username}) - name = user.get("last_name") - return name + + user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)}) + client.close() + + if not user or not user.get("last_name"): + return "" + + return dp.decrypt_text(user.get("last_name")) def get_all_users(): - """ - Retrieve all users from the database. - Administrative function for user management. - - Returns: - list: List of all user documents - """ + """Retrieve all users from the database.""" try: client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) @@ -888,65 +772,58 @@ def get_all_users(): except Exception as e: return [] + def update_password(username, new_password): - """ - Update a user's password with a new one. - - Args: - username (str): Username of the user - new_password (str): New password to set - - Returns: - bool: True if password was updated successfully, False otherwise - """ + """Update a user's password with a new one.""" try: if not check_password_strength(new_password): return False - + client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - - # Hash the new password + hashed_password = hashing(new_password) - - # Update the user's password + result = users.update_one( - {'Username': username}, + {'Username': dp.encrypt_text(username)}, {'$set': {'Password': hashed_password}} ) - + if result.matched_count == 0: + result = users.update_one( + {'username': dp.encrypt_text(username)}, + {'$set': {'Password': hashed_password}} + ) + client.close() return result.modified_count > 0 except Exception as e: print(f"Error updating password: {e}") return False - + + def update_user_name(username, name, last_name): - """ - Update a user's name and last name. - - Args: - username (str): Username of the user - name (str): New first name - last_name (str): New last name - - Returns: - bool: True if updated successfully, False otherwise - """ + """Update a user's name and last name.""" try: - name_alias = build_name_synonym(name, last_name) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) db = _get_tenant_db(client) users = db['users'] - + + safe_name = dp.encrypt_text(name.strip()) if name else '' + safe_last_name = dp.encrypt_text(last_name.strip()) if last_name else '' + result = users.update_one( - {'Username': username}, - {'$set': {'name': name_alias, 'last_name': ''}} + {'Username': dp.encrypt_text(username)}, + {'$set': {'name': safe_name, 'last_name': safe_last_name}} ) - + if result.matched_count == 0: + result = users.update_one( + {'username': dp.encrypt_text(username)}, + {'$set': {'name': safe_name, 'last_name': safe_last_name}} + ) + client.close() return True except Exception as e: print(f"Error updating user name: {e}") - return False + return False \ No newline at end of file