March 10 2026: fixed some bugs, and cleaned code

This commit is contained in:
2026-03-10 16:35:41 -04:00
parent f4e67726fd
commit c4c563f7c0
3 changed files with 125 additions and 92 deletions
+19 -7
View File
@@ -7,6 +7,7 @@ from datetime import datetime, date, time, timedelta
from sqlalchemy import text from sqlalchemy import text
from user_agents import parse from user_agents import parse
import io, os, base64, re, uuid, requests, json, qrcode, math, traceback, googlemaps import io, os, base64, re, uuid, requests, json, qrcode, math, traceback, googlemaps
import time as _time # Separate from datetime.time — used for performance timing (time.time())
import openpyxl.cell.cell import openpyxl.cell.cell
from PIL import Image, ImageDraw from PIL import Image, ImageDraw
from math import radians, sin, cos, asin, sqrt from math import radians, sin, cos, asin, sqrt
@@ -14,7 +15,6 @@ from dotenv import load_dotenv
# Import the logging handler # Import the logging handler
from logger_handler import AppLogger, log_user_activity, log_database_operations from logger_handler import AppLogger, log_user_activity, log_database_operations
from single_checkin_calculator import SingleCheckInCalculator
from working_hours_calculator import WorkingHoursCalculator, round_time_to_quarter_hour, convert_minutes_to_base100, round_base100_hours from working_hours_calculator import WorkingHoursCalculator, round_time_to_quarter_hour, convert_minutes_to_base100, round_base100_hours
from payroll_excel_exporter import PayrollExcelExporter from payroll_excel_exporter import PayrollExcelExporter
from enhanced_payroll_excel_exporter import EnhancedPayrollExcelExporter from enhanced_payroll_excel_exporter import EnhancedPayrollExcelExporter
@@ -11193,18 +11193,27 @@ def create_tables():
# Create default admin user if not exists # Create default admin user if not exists
admin = User.query.filter_by(username='admin').first() admin = User.query.filter_by(username='admin').first()
if not admin: if not admin:
default_password = os.environ.get('DEFAULT_ADMIN_PASSWORD', 'admin123')
admin = User( admin = User(
full_name='System Administrator', full_name='System Administrator',
email='admin@example.com', email='admin@example.com',
username='admin', username='admin',
role='admin' role='admin'
) )
admin.set_password('admin123') # Change this in production admin.set_password(default_password)
db.session.add(admin) db.session.add(admin)
db.session.commit() db.session.commit()
# Log admin user creation # Warn if the insecure default password is still in use
logger_handler.logger.info("Default admin user created during initialization") if default_password == 'admin123':
print("⚠️ WARNING: Default admin password 'admin123' is in use. "
"Set DEFAULT_ADMIN_PASSWORD in your .env file before going to production.")
logger_handler.logger.warning(
"Default admin user created with insecure default password. "
"Set DEFAULT_ADMIN_PASSWORD environment variable."
)
else:
logger_handler.logger.info("Default admin user created during initialization")
# Initialize logging table # Initialize logging table
logger_handler._create_log_table() logger_handler._create_log_table()
@@ -11401,7 +11410,7 @@ def log_response_info(response):
# Log slow requests (over 5 seconds) # Log slow requests (over 5 seconds)
if hasattr(request, 'start_time'): if hasattr(request, 'start_time'):
duration = time.time() - request.start_time duration = _time.time() - request.start_time
if duration > 5.0: if duration > 5.0:
logger_handler.logger.warning(f"Slow request: {request.path} took {duration:.2f} seconds") logger_handler.logger.warning(f"Slow request: {request.path} took {duration:.2f} seconds")
@@ -11475,12 +11484,12 @@ def log_slow_query_performance():
"""Monitor and log slow query performance""" """Monitor and log slow query performance"""
@app.before_request @app.before_request
def before_request(): def before_request():
g.start_time = time.time() g.start_time = _time.time()
@app.after_request @app.after_request
def after_request(response): def after_request(response):
if hasattr(g, 'start_time'): if hasattr(g, 'start_time'):
duration = time.time() - g.start_time duration = _time.time() - g.start_time
# Log slow requests (over 2 seconds) # Log slow requests (over 2 seconds)
if duration > 2.0: if duration > 2.0:
@@ -11504,6 +11513,9 @@ if __name__ == '__main__':
# Initialize database and logging # Initialize database and logging
create_tables() create_tables()
# Register slow query performance monitoring hooks
log_slow_query_performance()
# Initialize performance optimizations # Initialize performance optimizations
print("🚀 Initializing performance optimizations...") print("🚀 Initializing performance optimizations...")
cached_query = initialize_performance_optimizations(app, db, logger_handler) cached_query = initialize_performance_optimizations(app, db, logger_handler)
+44 -57
View File
@@ -856,6 +856,50 @@ class AppLogger:
severity='INFO' severity='INFO'
) )
def verify_log_table_exists(self):
"""Verify that the log_events table exists and has the correct structure"""
try:
check_table_sql = """
SELECT COUNT(*) as table_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'log_events'
"""
result = self.db.session.execute(text(check_table_sql)).fetchone()
if result.table_exists == 0:
print("⚠️ log_events table does not exist. Creating it now...")
self._create_log_table()
return True
count_sql = "SELECT COUNT(*) as record_count FROM log_events"
count_result = self.db.session.execute(text(count_sql)).fetchone()
print(f"✅ log_events table exists with {count_result.record_count} records")
return True
except Exception as e:
print(f"❌ Error verifying log table: {e}")
return False
def log_modal_interaction(self, event_type, description, additional_data=None):
"""Log modal interactions for debugging"""
try:
context = self._get_request_context()
event_data = {
'interaction_type': event_type,
'event_timestamp': datetime.now().isoformat(),
'request_context': context
}
if additional_data:
event_data['additional_data'] = additional_data
message = f"Modal interaction: {event_type} - {description}"
self._log_to_database(
event_type='modal_interaction',
event_category='ui',
description=message,
event_data=event_data,
severity='INFO'
)
except Exception as e:
print(f"Error logging modal interaction: {e}")
# DECORATOR FUNCTIONS FOR AUTOMATIC LOGGING # DECORATOR FUNCTIONS FOR AUTOMATIC LOGGING
def log_user_activity(activity_type): def log_user_activity(activity_type):
@@ -917,63 +961,6 @@ def log_database_operations(operation_name):
return decorated_function return decorated_function
return decorator return decorator
def verify_log_table_exists(self):
"""Verify that the log_events table exists and has the correct structure"""
try:
# Check if table exists
check_table_sql = """
SELECT COUNT(*) as table_exists
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'log_events'
"""
result = self.db.session.execute(text(check_table_sql)).fetchone()
if result.table_exists == 0:
print("⚠️ log_events table does not exist. Creating it now...")
self._create_log_table()
return True
# Check if table has records
count_sql = "SELECT COUNT(*) as record_count FROM log_events"
count_result = self.db.session.execute(text(count_sql)).fetchone()
print(f"✅ log_events table exists with {count_result.record_count} records")
return True
except Exception as e:
print(f"❌ Error verifying log table: {e}")
return False
def log_modal_interaction(self, event_type, description, additional_data=None):
"""Log modal interactions for debugging"""
try:
context = self._get_request_context()
event_data = {
'interaction_type': event_type,
'event_timestamp': datetime.now().isoformat(),
'request_context': context
}
if additional_data:
event_data['additional_data'] = additional_data
message = f"Modal interaction: {event_type} - {description}"
# Log to database
self._log_to_database(
event_type='modal_interaction',
event_category='ui',
description=message,
event_data=event_data,
severity='INFO'
)
except Exception as e:
print(f"Error logging modal interaction: {e}")
# INITIALIZATION FUNCTION # INITIALIZATION FUNCTION
def init_logging(app, db): def init_logging(app, db):
"""Initialize the logging system with the Flask app""" """Initialize the logging system with the Flask app"""
+61 -27
View File
@@ -811,23 +811,57 @@ document.addEventListener('DOMContentLoaded', function() {
} }
function displayEmployeeAutocompleteResults(employees) { function displayEmployeeAutocompleteResults(employees) {
// Clear previous results safely
autocompleteResultsFilter.innerHTML = '';
if (employees.length === 0) { if (employees.length === 0) {
autocompleteResultsFilter.innerHTML = '<div class="autocomplete-item-filter" style="cursor: default; color: #999;">No employees found</div>'; const noResult = document.createElement('div');
positionDropdown(); noResult.className = 'autocomplete-item-filter';
autocompleteResultsFilter.classList.add('show'); noResult.style.cssText = 'cursor:default;color:#999;';
return; noResult.textContent = 'No employees found';
autocompleteResultsFilter.appendChild(noResult);
} else {
employees.forEach(function(emp) {
const isUnregistered = (emp.lastName === '(no record)');
const displayName = isUnregistered
? 'ID: ' + emp.id
: emp.lastName + ', ' + emp.firstName;
const item = document.createElement('div');
item.className = 'autocomplete-item-filter';
const info = document.createElement('div');
info.className = 'employee-info-filter';
const nameSpan = document.createElement('span');
nameSpan.className = 'employee-name-filter';
if (isUnregistered) {
nameSpan.textContent = 'ID: ' + emp.id + ' ';
const em = document.createElement('em');
em.style.color = '#94a3b8';
em.textContent = '(no record)';
nameSpan.appendChild(em);
} else {
nameSpan.textContent = displayName;
}
const idSpan = document.createElement('span');
idSpan.className = 'employee-id-filter';
idSpan.textContent = 'ID: ' + emp.id;
info.appendChild(nameSpan);
info.appendChild(idSpan);
item.appendChild(info);
// Closure captures values safely regardless of special characters in names
item.addEventListener('click', (function(id, name) {
return function() { selectEmployeeFilter(id, name); };
}(emp.id, displayName)));
autocompleteResultsFilter.appendChild(item);
});
} }
const html = employees.map(emp => `
<div class="autocomplete-item-filter" onclick="selectEmployeeFilter(${emp.id}, '${emp.lastName}, ${emp.firstName}')">
<div class="employee-info-filter">
<span class="employee-name-filter">${emp.lastName}, ${emp.firstName}</span>
<span class="employee-id-filter">ID: ${emp.id}</span>
</div>
</div>
`).join('');
autocompleteResultsFilter.innerHTML = html;
positionDropdown(); positionDropdown();
autocompleteResultsFilter.classList.add('show'); autocompleteResultsFilter.classList.add('show');
} }
@@ -853,19 +887,19 @@ document.addEventListener('DOMContentLoaded', function() {
// Submit the form to refresh with cleared filter // Submit the form to refresh with cleared filter
employeeSearchFilter.closest('form').submit(); employeeSearchFilter.closest('form').submit();
}; };
// Reposition dropdown on scroll or resize
window.addEventListener('scroll', function() {
if (autocompleteResultsFilter.classList.contains('show')) {
positionDropdown();
}
}, true);
window.addEventListener('resize', function() {
if (autocompleteResultsFilter.classList.contains('show')) {
positionDropdown();
}
});
})(); })();
// Reposition dropdown on scroll or resize
window.addEventListener('scroll', function() {
if (autocompleteResultsFilter.classList.contains('show')) {
positionDropdown();
}
}, true);
window.addEventListener('resize', function() {
if (autocompleteResultsFilter.classList.contains('show')) {
positionDropdown();
}
});
</script> </script>
{% endblock %} {% endblock %}