Spaces:
Running
Running
| # utils.py | |
| # Shared helper functions and decorators | |
| from functools import wraps | |
| from flask import session, redirect, url_for, request, jsonify | |
| from datetime import datetime, timezone | |
| import random | |
| import string | |
| # ββ AUTH DECORATORS ββββββββββββββββββββββββββββββββββββββββ | |
| def login_required(f): | |
| """Redirect to login if user is not in session. Returns 401 for API calls.""" | |
| def decorated(*args, **kwargs): | |
| if "user_id" not in session: | |
| if request.is_json or request.path.startswith("/api/"): | |
| return jsonify({"success": False, "message": "Login required."}), 401 | |
| return redirect(url_for("auth.login_page")) | |
| return f(*args, **kwargs) | |
| return decorated | |
| def admin_required(f): | |
| """Returns 403 if the current user is not an admin.""" | |
| def decorated(*args, **kwargs): | |
| if not session.get("is_admin"): | |
| return jsonify({"success": False, "message": "Admin access required."}), 403 | |
| return f(*args, **kwargs) | |
| return decorated | |
| # ββ TIME HELPERS βββββββββββββββββββββββββββββββββββββββββββ | |
| def time_ago(dt: datetime) -> str: | |
| """Convert a datetime to a human-readable 'X ago' string.""" | |
| now = datetime.now(timezone.utc) | |
| if dt.tzinfo is None: | |
| dt = dt.replace(tzinfo=timezone.utc) | |
| seconds = int((now - dt).total_seconds()) | |
| if seconds < 60: | |
| return "Just now" | |
| elif seconds < 3600: | |
| m = seconds // 60 | |
| return f"{m} minute{'s' if m > 1 else ''} ago" | |
| elif seconds < 86400: | |
| h = seconds // 3600 | |
| return f"{h} hour{'s' if h > 1 else ''} ago" | |
| elif seconds < 604800: | |
| d = seconds // 86400 | |
| return f"{d} day{'s' if d > 1 else ''} ago" | |
| else: | |
| return dt.strftime("%b %d, %Y") | |
| # ββ ID GENERATOR βββββββββββββββββββββββββββββββββββββββββββ | |
| def generate_student_id() -> str: | |
| """Generate a unique student ID like STU123456.""" | |
| return "STU" + "".join(random.choices(string.digits, k=6)) | |
| # ββ PAGINATION HELPER ββββββββββββββββββββββββββββββββββββββ | |
| def get_pagination_args(): | |
| """Extract page and limit from request args.""" | |
| page = max(1, int(request.args.get("page", 1))) | |
| limit = min(50, int(request.args.get("limit", 10))) | |
| return page, limit, (page - 1) * limit |