From fcd3c92850ec49cddd640b91c974f97058f8960e Mon Sep 17 00:00:00 2001 From: NguyenND Date: Tue, 18 Nov 2025 17:04:31 -0500 Subject: [PATCH] Implement create (import) QR code in bulk --- app.py | 142 ++++++++++++++++++++++++++++++++++----- utils/__init__.py | 54 --------------- utils/auth_decorators.py | 125 ---------------------------------- utils/date_time_utils.py | 0 utils/helpers.py | 0 utils/location_utils.py | 0 utils/qr_utils.py | 0 utils/validation.py | 132 ------------------------------------ 8 files changed, 124 insertions(+), 329 deletions(-) delete mode 100644 utils/__init__.py delete mode 100644 utils/auth_decorators.py delete mode 100644 utils/date_time_utils.py delete mode 100644 utils/helpers.py delete mode 100644 utils/location_utils.py delete mode 100644 utils/qr_utils.py delete mode 100644 utils/validation.py diff --git a/app.py b/app.py index 03b7dfd..5d1308e 100644 --- a/app.py +++ b/app.py @@ -27,24 +27,6 @@ from turnstile_utils import turnstile_utils from db_performance_optimization import initialize_performance_optimizations from app_performance_middleware import PerformanceMonitor -from utils.validation import ( - is_valid_role, - has_admin_privileges, - has_staff_level_access, - has_export_permissions, - get_role_permissions, - VALID_ROLES, - STAFF_LEVEL_ROLES -) - -from utils.auth_decorators import ( - login_required, - admin_required, - staff_or_admin_required, - project_manager_required, - payroll_required -) - # Initialize Flask application app = Flask(__name__) app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY') @@ -229,6 +211,12 @@ def cache_coordinates(address, lat, lng, accuracy): except Exception as e: print(f"⚠️ Error caching coordinates: {e}") +# Valid user roles with new additions +VALID_ROLES = ['admin', 'staff', 'payroll', 'project_manager'] + +# Roles that have staff-level permissions (non-admin roles) +STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager'] + # Import and initialize models from models import set_db User, QRCode, QRCodeStyle, Project, AttendanceData, Employee, TimeAttendance, UserProjectPermission, UserLocationPermission = set_db(db) @@ -236,7 +224,82 @@ User, QRCode, QRCodeStyle, Project, AttendanceData, Employee, TimeAttendance, Us # Initialize the logging system logger_handler = AppLogger(app, db) +# Utility functions +def is_valid_role(role): + """Check if role is valid""" + return role in VALID_ROLES +def has_admin_privileges(role): + """Check if role has admin privileges""" + return role == 'admin' + +def has_staff_level_access(role): + """Check if role has staff-level access (includes new roles)""" + return role in STAFF_LEVEL_ROLES + +def get_role_permissions(role): + """Get permissions description for a role""" + permissions = { + 'admin': { + 'title': 'Administrator Permissions', + 'permissions': [ + 'Full QR code management (create, edit, delete)', + 'Complete user management capabilities', + 'System configuration access', + 'View all system analytics', + 'Bulk operations and data export', + 'Access to all admin features' + ], + 'restrictions': ['With great power comes great responsibility!'] + }, + 'staff': { + 'title': 'Staff User Permissions', + 'permissions': [ + 'Create and edit QR codes', + 'View all QR codes in the system', + 'Download QR code images', + 'Update personal profile information', + ], + 'restrictions': [ + 'Cannot delete QR codes', + 'Cannot manage other users', + 'Cannot access admin settings' + ] + }, + 'payroll': { + 'title': 'Payroll Specialist Permissions', + 'permissions': [ + 'Create and edit QR codes', + 'View all QR codes in the system', + 'Download QR code images', + 'Update personal profile information', + 'Access dashboard and reports', + 'Same permissions as Staff (additional features coming soon)' + ], + 'restrictions': [ + 'Cannot delete QR codes', + 'Cannot manage other users', + 'Cannot access admin settings' + ] + }, + 'project_manager': { + 'title': 'Project Manager Permissions', + 'permissions': [ + 'Create and edit QR codes', + 'View all QR codes in the system', + 'Download QR code images', + 'Update personal profile information', + 'Access dashboard and reports', + 'Same permissions as Staff (additional features coming soon)' + ], + 'restrictions': [ + 'Cannot delete QR codes', + 'Cannot manage other users', + 'Cannot access admin settings' + ] + } + } + return permissions.get(role, {}) def log_google_maps_usage(operation_type): """Log Google Maps API usage for monitoring""" @@ -1291,6 +1354,49 @@ def format_time_interval(minutes): else: return f"{days}d {remaining_hours}h" +# Authentication decorator +def login_required(f): + """Decorator to ensure user is logged in""" + @wraps(f) + def decorated_function(*args, **kwargs): + if 'user_id' not in session: + flash('Please log in to access this page.', 'error') + return redirect(url_for('login')) + return f(*args, **kwargs) + return decorated_function + +def admin_required(f): + """Decorator to ensure user has admin privileges""" + @wraps(f) + def decorated_function(*args, **kwargs): + if 'username' not in session: + flash('Please log in to access this page.', 'error') + return redirect(url_for('login')) + + user_role = session.get('role') + if not has_admin_privileges(user_role): + flash('Administrator privileges required for this action.', 'error') + return redirect(url_for('dashboard')) + + return f(*args, **kwargs) + return decorated_function + +def staff_or_admin_required(f): + """Decorator to ensure user has staff-level or admin privileges""" + @wraps(f) + def decorated_function(*args, **kwargs): + if 'username' not in session: + flash('Please log in to access this page.', 'error') + return redirect(url_for('login')) + + user_role = session.get('role') + if not (has_admin_privileges(user_role) or has_staff_level_access(user_role)): + flash('Insufficient privileges to access this page.', 'error') + return redirect(url_for('dashboard')) + + return f(*args, **kwargs) + return decorated_function + # Add this helper function to check admin requirements more safely def is_admin_user(user_id): """Helper function to safely check if user is admin""" diff --git a/utils/__init__.py b/utils/__init__.py deleted file mode 100644 index f2c4c13..0000000 --- a/utils/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Utilities Package for QR Attendance Management System -==================================================== - -This package provides consolidated utility functions extracted from app.py -for better code organization and reusability. - -Modules: - - auth_decorators: Authentication and authorization decorators - - validation: Role validation and permissions checking - - location_utils: Location and geocoding utilities - - qr_utils: QR code generation utilities - - date_time_utils: Date and time formatting utilities - - helpers: General helper functions -""" - -# Authentication decorators -from .auth_decorators import ( - login_required, - admin_required, - staff_or_admin_required, - project_manager_required, - payroll_required -) - -# Validation functions -from .validation import ( - is_valid_role, - has_admin_privileges, - has_staff_level_access, - has_export_permissions, - get_role_permissions, - VALID_ROLES, - STAFF_LEVEL_ROLES -) - -# Export all for easy importing -__all__ = [ - # Decorators - 'login_required', - 'admin_required', - 'staff_or_admin_required', - 'project_manager_required', - 'payroll_required', - - # Validation - 'is_valid_role', - 'has_admin_privileges', - 'has_staff_level_access', - 'has_export_permissions', - 'get_role_permissions', - 'VALID_ROLES', - 'STAFF_LEVEL_ROLES', -] \ No newline at end of file diff --git a/utils/auth_decorators.py b/utils/auth_decorators.py deleted file mode 100644 index 3a8dcdd..0000000 --- a/utils/auth_decorators.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Authentication and Authorization Decorators -========================================== - -Consolidated authentication decorators to eliminate duplication. -Extracted from app.py for better code organization. -""" - -from functools import wraps -from flask import session, flash, redirect, url_for -from utils.validation import has_admin_privileges, has_staff_level_access - -def login_required(f): - """ - Decorator to ensure user is logged in - - Usage: - @app.route('/dashboard') - @login_required - def dashboard(): - return render_template('dashboard.html') - """ - @wraps(f) - def decorated_function(*args, **kwargs): - if 'user_id' not in session: - flash('Please log in to access this page.', 'error') - return redirect(url_for('login')) - return f(*args, **kwargs) - return decorated_function - -def admin_required(f): - """ - Decorator to ensure user has admin privileges - - Usage: - @app.route('/admin/users') - @admin_required - def manage_users(): - return render_template('users.html') - """ - @wraps(f) - def decorated_function(*args, **kwargs): - if 'username' not in session: - flash('Please log in to access this page.', 'error') - return redirect(url_for('login')) - - user_role = session.get('role') - if not has_admin_privileges(user_role): - flash('Administrator privileges required for this action.', 'error') - return redirect(url_for('dashboard')) - - return f(*args, **kwargs) - return decorated_function - -def staff_or_admin_required(f): - """ - Decorator to ensure user has staff-level or admin privileges - - Usage: - @app.route('/attendance/records') - @staff_or_admin_required - def view_records(): - return render_template('records.html') - """ - @wraps(f) - def decorated_function(*args, **kwargs): - if 'username' not in session: - flash('Please log in to access this page.', 'error') - return redirect(url_for('login')) - - user_role = session.get('role') - if not (has_admin_privileges(user_role) or has_staff_level_access(user_role)): - flash('Insufficient privileges to access this page.', 'error') - return redirect(url_for('dashboard')) - - return f(*args, **kwargs) - return decorated_function - -def project_manager_required(f): - """ - Decorator to ensure user is project manager or admin - - Usage: - @app.route('/projects/dashboard') - @project_manager_required - def project_dashboard(): - return render_template('project_dashboard.html') - """ - @wraps(f) - def decorated_function(*args, **kwargs): - if 'username' not in session: - flash('Please log in to access this page.', 'error') - return redirect(url_for('login')) - - user_role = session.get('role') - if user_role not in ['admin', 'project_manager']: - flash('Project Manager access required.', 'error') - return redirect(url_for('dashboard')) - - return f(*args, **kwargs) - return decorated_function - -def payroll_required(f): - """ - Decorator to ensure user is payroll staff or admin - - Usage: - @app.route('/payroll/process') - @payroll_required - def process_payroll(): - return render_template('payroll.html') - """ - @wraps(f) - def decorated_function(*args, **kwargs): - if 'username' not in session: - flash('Please log in to access this page.', 'error') - return redirect(url_for('login')) - - user_role = session.get('role') - if user_role not in ['admin', 'payroll']: - flash('Payroll access required.', 'error') - return redirect(url_for('dashboard')) - - return f(*args, **kwargs) - return decorated_function \ No newline at end of file diff --git a/utils/date_time_utils.py b/utils/date_time_utils.py deleted file mode 100644 index e69de29..0000000 diff --git a/utils/helpers.py b/utils/helpers.py deleted file mode 100644 index e69de29..0000000 diff --git a/utils/location_utils.py b/utils/location_utils.py deleted file mode 100644 index e69de29..0000000 diff --git a/utils/qr_utils.py b/utils/qr_utils.py deleted file mode 100644 index e69de29..0000000 diff --git a/utils/validation.py b/utils/validation.py deleted file mode 100644 index 17afe62..0000000 --- a/utils/validation.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -Validation Utilities for QR Attendance Management System -======================================================== - -Role validation and permissions checking functions. -Extracted from app.py for better code organization. -""" - -# Valid user roles -VALID_ROLES = ['admin', 'staff', 'payroll', 'project_manager'] -STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager'] - -def is_valid_role(role): - """ - Check if role is valid - - Args: - role (str): User role to validate - - Returns: - bool: True if role is valid - """ - return role in VALID_ROLES - -def has_admin_privileges(role): - """ - Check if role has admin privileges - - Args: - role (str): User role to check - - Returns: - bool: True if role is admin - """ - return role == 'admin' - -def has_staff_level_access(role): - """ - Check if role has staff-level access (includes payroll, project_manager) - - Args: - role (str): User role to check - - Returns: - bool: True if role has staff-level permissions - """ - return role in STAFF_LEVEL_ROLES - -def has_export_permissions(role): - """ - Check if user role has export permissions - - Args: - role (str): User role to check - - Returns: - bool: True if role can export data - """ - return role in ['admin', 'payroll'] - -def get_role_permissions(role): - """ - Get permissions description for a role - - Args: - role (str): User role - - Returns: - dict: Role permissions with title, permissions list, and restrictions - """ - permissions = { - 'admin': { - 'title': 'Administrator Permissions', - 'permissions': [ - 'Full QR code management (create, edit, delete)', - 'Complete user management capabilities', - 'System configuration access', - 'View all system analytics', - 'Bulk operations and data export', - 'Access to all admin features' - ], - 'restrictions': ['With great power comes great responsibility!'] - }, - 'staff': { - 'title': 'Staff User Permissions', - 'permissions': [ - 'View QR codes and attendance records', - 'Check-in to attendance', - 'View own attendance history' - ], - 'restrictions': [ - 'Cannot create or modify QR codes', - 'Cannot manage users', - 'Limited export capabilities' - ] - }, - 'payroll': { - 'title': 'Payroll Specialist Permissions', - 'permissions': [ - 'Full payroll processing access', - 'Export attendance and payroll data', - 'View all attendance records', - 'Generate payroll reports' - ], - 'restrictions': [ - 'Cannot manage QR codes', - 'Cannot manage users', - 'Read-only access to configurations' - ] - }, - 'project_manager': { - 'title': 'Project Manager Permissions', - 'permissions': [ - 'View assigned project data', - 'View assigned location data', - 'Generate project reports', - 'Monitor project attendance' - ], - 'restrictions': [ - 'Access limited to assigned projects/locations', - 'Cannot modify QR codes', - 'Cannot manage users', - 'Read-only access' - ] - } - } - - return permissions.get(role, { - 'title': 'Unknown Role', - 'permissions': [], - 'restrictions': ['Invalid role specified'] - }) \ No newline at end of file