diff --git a/app/routes/auth.py b/app/routes/auth.py index fd3e4ac..8d617d7 100644 --- a/app/routes/auth.py +++ b/app/routes/auth.py @@ -3,8 +3,11 @@ from flask_login import login_user, logout_user, login_required, current_user from urllib.parse import urlparse from app import db from app.models.user import User -from app.utils.forms import LoginForm, UserForm +from app.utils.forms import LoginForm, UserForm, ProfileForm from app.utils.decorators import admin_required +import logging + +logger = logging.getLogger(__name__) bp = Blueprint('auth', __name__, url_prefix='/auth') @@ -55,6 +58,57 @@ def logout(): return redirect(url_for('auth.login')) +@bp.route('/profile', methods=['GET', 'POST']) +@login_required +def profile(): + """User profile page — view stats and update email/password.""" + from app.models.inspection import Inspection + from app.models.issue import Issue + + form = ProfileForm(user=current_user, obj=current_user) + + if form.validate_on_submit(): + current_user.email = form.email.data + + if form.new_password.data: + current_user.set_password(form.new_password.data) + logger.info('AUTH | profile_password_change | user_id=%s username=%s', + current_user.id, current_user.username) + + db.session.commit() + logger.info('AUTH | profile_update | user_id=%s username=%s email=%s', + current_user.id, current_user.username, current_user.email) + flash('Profile updated successfully.', 'success') + return redirect(url_for('auth.profile')) + + # ── Activity stats ──────────────────────────────────────────────────── + total_inspections = Inspection.query.filter_by(inspector_id=current_user.id).count() + completed_inspections = Inspection.query.filter_by( + inspector_id=current_user.id, status='completed' + ).count() + + recent_inspections = ( + Inspection.query + .filter_by(inspector_id=current_user.id) + .order_by(Inspection.inspection_date.desc()) + .limit(5) + .all() + ) + + open_issues = Issue.query.filter_by( + assigned_to=current_user.id, status='open' + ).count() if hasattr(Issue, 'assigned_to') else 0 + + return render_template( + 'auth/profile.html', + form=form, + total_inspections=total_inspections, + completed_inspections=completed_inspections, + recent_inspections=recent_inspections, + open_issues=open_issues, + ) + + @bp.route('/users') @login_required @admin_required @@ -78,6 +132,8 @@ def create_user(): user.set_password(form.password.data) db.session.add(user) db.session.commit() + logger.info('AUTH | user_create | admin_id=%s admin=%s new_user=%s role=%s', + current_user.id, current_user.username, user.username, user.role) flash(f'User {user.username} created successfully.', 'success') return redirect(url_for('auth.list_users')) @@ -100,6 +156,8 @@ def edit_user(user_id): user.set_password(form.password.data) db.session.commit() + logger.info('AUTH | user_edit | admin_id=%s admin=%s target_user_id=%s target_user=%s', + current_user.id, current_user.username, user.id, user.username) flash(f'User {user.username} updated successfully.', 'success') return redirect(url_for('auth.list_users')) @@ -130,5 +188,7 @@ def delete_user(user_id): username = user.username db.session.delete(user) db.session.commit() + logger.info('AUTH | user_delete | admin_id=%s admin=%s deleted_user=%s', + current_user.id, current_user.username, username) flash(f'User {username} deleted successfully.', 'success') - return redirect(url_for('auth.list_users')) + return redirect(url_for('auth.list_users')) \ No newline at end of file diff --git a/app/templates/auth/profile.html b/app/templates/auth/profile.html new file mode 100644 index 0000000..a86faf0 --- /dev/null +++ b/app/templates/auth/profile.html @@ -0,0 +1,212 @@ +{% extends "base.html" %} + +{% block title %}My Profile{% endblock %} + +{% block content %} +
Manage your account information and review your activity.
+{{ current_user.email }}
+ + + {{ current_user.role | title }} + +| Date | +Facility | +Template | +Score | +Status | ++ |
|---|---|---|---|---|---|
| {{ insp.inspection_date.strftime('%Y-%m-%d') }} | +{{ insp.facility.name if insp.facility else '—' }} | +{{ insp.template.name if insp.template else '—' }} | ++ {% if insp.overall_score is not none %} + + {{ "%.1f"|format(insp.overall_score) }}% + + {% else %} + — + {% endif %} + | ++ + {{ insp.status | replace('_', ' ') | title }} + + | ++ + + + | +