Refactor MongoDB client imports and implement lazy loading for main items in UI
This commit is contained in:
+1
-1
@@ -33,7 +33,6 @@ import ausleihung as au
|
|||||||
import audit_log as al
|
import audit_log as al
|
||||||
import datetime
|
import datetime
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from pymongo import MongoClient
|
|
||||||
from bson.objectid import ObjectId
|
from bson.objectid import ObjectId
|
||||||
from urllib.parse import urlparse, urlunparse
|
from urllib.parse import urlparse, urlunparse
|
||||||
import requests
|
import requests
|
||||||
@@ -59,6 +58,7 @@ import subprocess
|
|||||||
# Set base directory and centralized settings
|
# Set base directory and centralized settings
|
||||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
import settings as cfg
|
import settings as cfg
|
||||||
|
from settings import MongoClient
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__, static_folder='static') # Correctly set static folder
|
app = Flask(__name__, static_folder='static') # Correctly set static folder
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,6 @@ Sammlungsstruktur:
|
|||||||
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
|
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
|
||||||
For commercial licensing inquiries: https://github.com/AIIrondev
|
For commercial licensing inquiries: https://github.com/AIIrondev
|
||||||
'''
|
'''
|
||||||
from pymongo import MongoClient
|
|
||||||
from bson.objectid import ObjectId
|
from bson.objectid import ObjectId
|
||||||
import datetime
|
import datetime
|
||||||
import pytz
|
import pytz
|
||||||
@@ -34,6 +33,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
import settings as cfg
|
import settings as cfg
|
||||||
|
from settings import MongoClient
|
||||||
|
|
||||||
# Add this helper function after imports
|
# Add this helper function after imports
|
||||||
def ensure_timezone_aware(dt):
|
def ensure_timezone_aware(dt):
|
||||||
|
|||||||
+1
-1
@@ -26,10 +26,10 @@ Collection Structure:
|
|||||||
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
|
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
|
||||||
For commercial licensing inquiries: https://github.com/AIIrondev
|
For commercial licensing inquiries: https://github.com/AIIrondev
|
||||||
'''
|
'''
|
||||||
from pymongo import MongoClient
|
|
||||||
from bson.objectid import ObjectId
|
from bson.objectid import ObjectId
|
||||||
import datetime
|
import datetime
|
||||||
import settings as cfg
|
import settings as cfg
|
||||||
|
from settings import MongoClient
|
||||||
|
|
||||||
|
|
||||||
LIBRARY_ITEM_TYPES = ('book', 'cd', 'dvd', 'media')
|
LIBRARY_ITEM_TYPES = ('book', 'cd', 'dvd', 'media')
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ defaults for the web application and helper modules.
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from pymongo import MongoClient as _PyMongoClient
|
||||||
|
|
||||||
# Base directory of this Web package
|
# Base directory of this Web package
|
||||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
@@ -173,3 +176,21 @@ if not os.path.isabs(BACKUP_FOLDER):
|
|||||||
BACKUP_FOLDER = os.path.join(PROJECT_ROOT, BACKUP_FOLDER)
|
BACKUP_FOLDER = os.path.join(PROJECT_ROOT, BACKUP_FOLDER)
|
||||||
if not os.path.isabs(LOGS_FOLDER):
|
if not os.path.isabs(LOGS_FOLDER):
|
||||||
LOGS_FOLDER = os.path.join(PROJECT_ROOT, LOGS_FOLDER)
|
LOGS_FOLDER = os.path.join(PROJECT_ROOT, LOGS_FOLDER)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _shared_mongo_client():
|
||||||
|
return _PyMongoClient(
|
||||||
|
MONGODB_HOST,
|
||||||
|
MONGODB_PORT,
|
||||||
|
maxPoolSize=10,
|
||||||
|
minPoolSize=0,
|
||||||
|
connectTimeoutMS=5000,
|
||||||
|
serverSelectionTimeoutMS=5000,
|
||||||
|
retryWrites=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def MongoClient(*args, **kwargs):
|
||||||
|
"""Return a shared MongoDB client configured from this settings module."""
|
||||||
|
return _shared_mongo_client()
|
||||||
|
|||||||
+66
-11
@@ -725,9 +725,14 @@
|
|||||||
let allItems = [];
|
let allItems = [];
|
||||||
|
|
||||||
const MAIN_ITEMS_PAGE_SIZE = 120;
|
const MAIN_ITEMS_PAGE_SIZE = 120;
|
||||||
|
let mainItemsNextOffset = 0;
|
||||||
|
let mainItemsHasMore = false;
|
||||||
|
let mainItemsLoadingMore = false;
|
||||||
|
let mainItemsObserver = null;
|
||||||
|
let mainItemsSentinel = null;
|
||||||
|
|
||||||
function loadItems(offset = 0, append = false) {
|
function loadItems(offset = 0, append = false) {
|
||||||
fetch(`{{ url_for('get_items') }}?offset=${offset}&limit=${MAIN_ITEMS_PAGE_SIZE}`)
|
return fetch(`{{ url_for('get_items') }}?offset=${offset}&limit=${MAIN_ITEMS_PAGE_SIZE}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const itemsContainer = document.querySelector('#items-container');
|
const itemsContainer = document.querySelector('#items-container');
|
||||||
@@ -1009,7 +1014,7 @@
|
|||||||
populateFilterOptions('filter2-options', [...filter2Values].sort(), 2);
|
populateFilterOptions('filter2-options', [...filter2Values].sort(), 2);
|
||||||
populateFilterOptions('filter3-options', [...filter3Values].sort(), 3);
|
populateFilterOptions('filter3-options', [...filter3Values].sort(), 3);
|
||||||
|
|
||||||
// Start display from leftmost position (first card, which is now Z)
|
// Start display from leftmost position on full reload only.
|
||||||
if (!append) {
|
if (!append) {
|
||||||
itemsContainer.scrollLeft = 0;
|
itemsContainer.scrollLeft = 0;
|
||||||
}
|
}
|
||||||
@@ -1028,15 +1033,9 @@
|
|||||||
rebuildFilter3Options();
|
rebuildFilter3Options();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.has_more) {
|
mainItemsNextOffset = (data.offset || offset) + (data.count || pageItems.length);
|
||||||
const nextOffset = (data.offset || offset) + (data.count || pageItems.length);
|
mainItemsHasMore = Boolean(data.has_more);
|
||||||
const continueLoad = () => loadItems(nextOffset, true);
|
setupMainItemsLazyLoading();
|
||||||
if ('requestIdleCallback' in window) {
|
|
||||||
requestIdleCallback(continueLoad, { timeout: 250 });
|
|
||||||
} else {
|
|
||||||
setTimeout(continueLoad, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error fetching items:', error);
|
console.error('Error fetching items:', error);
|
||||||
@@ -1049,6 +1048,62 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureMainItemsSentinel() {
|
||||||
|
const itemsContainer = document.querySelector('#items-container');
|
||||||
|
if (!itemsContainer) return null;
|
||||||
|
|
||||||
|
if (!mainItemsSentinel) {
|
||||||
|
mainItemsSentinel = document.createElement('div');
|
||||||
|
mainItemsSentinel.id = 'main-items-sentinel';
|
||||||
|
mainItemsSentinel.style.flex = '0 0 1px';
|
||||||
|
mainItemsSentinel.style.width = '1px';
|
||||||
|
mainItemsSentinel.style.minWidth = '1px';
|
||||||
|
mainItemsSentinel.style.height = '1px';
|
||||||
|
mainItemsSentinel.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainItemsSentinel.parentNode) {
|
||||||
|
itemsContainer.appendChild(mainItemsSentinel);
|
||||||
|
} else {
|
||||||
|
itemsContainer.appendChild(mainItemsSentinel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemsContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupMainItemsLazyLoading() {
|
||||||
|
const itemsContainer = ensureMainItemsSentinel();
|
||||||
|
if (!itemsContainer) return;
|
||||||
|
|
||||||
|
if (mainItemsObserver) {
|
||||||
|
mainItemsObserver.disconnect();
|
||||||
|
mainItemsObserver = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainItemsHasMore) return;
|
||||||
|
|
||||||
|
mainItemsObserver = new IntersectionObserver(entries => {
|
||||||
|
if (!entries.some(entry => entry.isIntersecting)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainItemsLoadingMore || !mainItemsHasMore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainItemsLoadingMore = true;
|
||||||
|
loadItems(mainItemsNextOffset, true).finally(() => {
|
||||||
|
mainItemsLoadingMore = false;
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
root: itemsContainer,
|
||||||
|
threshold: 0.15,
|
||||||
|
rootMargin: '0px 220px 0px 0px'
|
||||||
|
});
|
||||||
|
|
||||||
|
mainItemsObserver.observe(mainItemsSentinel);
|
||||||
|
}
|
||||||
|
|
||||||
function searchByCode() {
|
function searchByCode() {
|
||||||
const searchInput = document.getElementById('code-search');
|
const searchInput = document.getElementById('code-search');
|
||||||
const rawSearchTerm = searchInput.value;
|
const rawSearchTerm = searchInput.value;
|
||||||
|
|||||||
@@ -3256,9 +3256,14 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
|
|
||||||
// Function to load items from server
|
// Function to load items from server
|
||||||
const MAIN_ADMIN_ITEMS_PAGE_SIZE = 120;
|
const MAIN_ADMIN_ITEMS_PAGE_SIZE = 120;
|
||||||
|
let mainAdminItemsNextOffset = 0;
|
||||||
|
let mainAdminItemsHasMore = false;
|
||||||
|
let mainAdminItemsLoadingMore = false;
|
||||||
|
let mainAdminItemsObserver = null;
|
||||||
|
let mainAdminItemsSentinel = null;
|
||||||
|
|
||||||
function loadItems(offset = 0, append = false) {
|
function loadItems(offset = 0, append = false) {
|
||||||
fetch(`{{ url_for('get_items') }}?offset=${offset}&limit=${MAIN_ADMIN_ITEMS_PAGE_SIZE}`)
|
return fetch(`{{ url_for('get_items') }}?offset=${offset}&limit=${MAIN_ADMIN_ITEMS_PAGE_SIZE}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const itemsContainer = document.querySelector('#items-container');
|
const itemsContainer = document.querySelector('#items-container');
|
||||||
@@ -3556,15 +3561,9 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
}
|
}
|
||||||
/* favorites render count removed */
|
/* favorites render count removed */
|
||||||
|
|
||||||
if (data.has_more) {
|
mainAdminItemsNextOffset = (data.offset || offset) + (data.count || pageItems.length);
|
||||||
const nextOffset = (data.offset || offset) + (data.count || pageItems.length);
|
mainAdminItemsHasMore = Boolean(data.has_more);
|
||||||
const continueLoad = () => loadItems(nextOffset, true);
|
setupMainAdminItemsLazyLoading();
|
||||||
if ('requestIdleCallback' in window) {
|
|
||||||
requestIdleCallback(continueLoad, { timeout: 250 });
|
|
||||||
} else {
|
|
||||||
setTimeout(continueLoad, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error fetching items:', error);
|
console.error('Error fetching items:', error);
|
||||||
@@ -3581,6 +3580,57 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureMainAdminItemsSentinel() {
|
||||||
|
const itemsContainer = document.querySelector('#items-container');
|
||||||
|
if (!itemsContainer) return null;
|
||||||
|
|
||||||
|
if (!mainAdminItemsSentinel) {
|
||||||
|
mainAdminItemsSentinel = document.createElement('div');
|
||||||
|
mainAdminItemsSentinel.id = 'main-admin-items-sentinel';
|
||||||
|
mainAdminItemsSentinel.style.flex = '0 0 1px';
|
||||||
|
mainAdminItemsSentinel.style.width = '1px';
|
||||||
|
mainAdminItemsSentinel.style.minWidth = '1px';
|
||||||
|
mainAdminItemsSentinel.style.height = '1px';
|
||||||
|
mainAdminItemsSentinel.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsContainer.appendChild(mainAdminItemsSentinel);
|
||||||
|
return itemsContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupMainAdminItemsLazyLoading() {
|
||||||
|
const itemsContainer = ensureMainAdminItemsSentinel();
|
||||||
|
if (!itemsContainer) return;
|
||||||
|
|
||||||
|
if (mainAdminItemsObserver) {
|
||||||
|
mainAdminItemsObserver.disconnect();
|
||||||
|
mainAdminItemsObserver = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainAdminItemsHasMore) return;
|
||||||
|
|
||||||
|
mainAdminItemsObserver = new IntersectionObserver(entries => {
|
||||||
|
if (!entries.some(entry => entry.isIntersecting)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainAdminItemsLoadingMore || !mainAdminItemsHasMore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainAdminItemsLoadingMore = true;
|
||||||
|
loadItems(mainAdminItemsNextOffset, true).finally(() => {
|
||||||
|
mainAdminItemsLoadingMore = false;
|
||||||
|
});
|
||||||
|
}, {
|
||||||
|
root: itemsContainer,
|
||||||
|
threshold: 0.15,
|
||||||
|
rootMargin: '0px 220px 0px 0px'
|
||||||
|
});
|
||||||
|
|
||||||
|
mainAdminItemsObserver.observe(mainAdminItemsSentinel);
|
||||||
|
}
|
||||||
|
|
||||||
function searchByCode() {
|
function searchByCode() {
|
||||||
const searchInput = document.getElementById('code-search');
|
const searchInput = document.getElementById('code-search');
|
||||||
const rawSearchTerm = searchInput.value;
|
const rawSearchTerm = searchInput.value;
|
||||||
|
|||||||
+1
-1
@@ -10,10 +10,10 @@ Provides methods for creating, validating, and retrieving user information.
|
|||||||
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
|
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
|
||||||
For commercial licensing inquiries: https://github.com/AIIrondev
|
For commercial licensing inquiries: https://github.com/AIIrondev
|
||||||
'''
|
'''
|
||||||
from pymongo import MongoClient
|
|
||||||
import hashlib
|
import hashlib
|
||||||
from bson.objectid import ObjectId
|
from bson.objectid import ObjectId
|
||||||
import settings as cfg
|
import settings as cfg
|
||||||
|
from settings import MongoClient
|
||||||
|
|
||||||
|
|
||||||
def normalize_student_card_id(card_id):
|
def normalize_student_card_id(card_id):
|
||||||
|
|||||||
Reference in New Issue
Block a user