From beaef9d405fb1f5fdef223ee6b413b0fdba266bc Mon Sep 17 00:00:00 2001 From: ranasalim1 Date: Tue, 21 Oct 2025 10:37:42 -0400 Subject: [PATCH] Implementation task --- App/controllers/auth.py | 22 ++++++++++++--- App/controllers/user.py | 33 ++++++++++++++++++++--- App/views/api_admin.py | 60 +++++++++++++++++++++++++++++++++++++++++ App/views/auth.py | 2 +- App/views/user.py | 22 +++++++++++++-- 5 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 App/views/api_admin.py diff --git a/App/controllers/auth.py b/App/controllers/auth.py index 658eb46..bd56e3b 100644 --- a/App/controllers/auth.py +++ b/App/controllers/auth.py @@ -1,16 +1,32 @@ -from flask_jwt_extended import create_access_token, jwt_required, JWTManager, get_jwt_identity, verify_jwt_in_request +from functools import wraps +from flask import jsonify +from flask_jwt_extended import create_access_token, current_user, jwt_required, JWTManager, get_jwt_identity, verify_jwt_in_request from App.models import User from App.database import db -def login(username, password): +def login(username, password, user_type): result = db.session.execute(db.select(User).filter_by(username=username)) user = result.scalar_one_or_none() - if user and user.check_password(password): + if user and user.check_password(password) and user.user_type == user_type: # Store ONLY the user id as a string in JWT 'sub' return create_access_token(identity=str(user.userID)) return None +def login_required(required_class): + def decorator(f): + @wraps(f) + @jwt_required() + def decorated_function(*args, **kwargs): + #Check if current_user is an instance of the required class + if not isinstance(current_user, required_class): + return jsonify({ + 'error': 'Unauthorized access', + 'message': f'User must be an instance of {required_class.__name__} to access this resource.' + }), 401 + return f(*args, **kwargs) + return decorated_function + return decorator def setup_jwt(app): jwt = JWTManager(app) diff --git a/App/controllers/user.py b/App/controllers/user.py index 47b346e..79394a4 100644 --- a/App/controllers/user.py +++ b/App/controllers/user.py @@ -3,9 +3,36 @@ def create_user(username, password): newuser = User(username=username, password=password) - db.session.add(newuser) - db.session.commit() - return newuser + try: + db.session.add(newuser) + db.session.commit() + return newuser + except Exception as e: + db.session.rollback() + print(f"Error creating user: {e}") + return None + +def create_staff(username, password): + newstaff = User(username=username, password=password) + try: + db.session.add(newstaff) + db.session.commit() + return newstaff + except Exception as e: + db.session.rollback() + print(f"Error creating staff user: {e}") + return None + +def create_student(username, password): + newstudent = User(username=username, password=password) + try: + db.session.add(newstudent) + db.session.commit() + return newstudent + except Exception as e: + db.session.rollback() + print(f"Error creating student user: {e}") + return None def get_user_by_username(username): result = db.session.execute(db.select(User).filter_by(username=username)) diff --git a/App/views/api_admin.py b/App/views/api_admin.py new file mode 100644 index 0000000..fbff7c8 --- /dev/null +++ b/App/views/api_admin.py @@ -0,0 +1,60 @@ +from flask import Blueprint, jsonify, request +from App.controllers import (staff_log_hours, + request_confirmation, + view_leaderboard, + view_accolades, + staff_confirm_hours, + staff_reject_hours, + update_leaderboard) + +listings_views = Blueprint('api_admin_views', __name__, template_folder='../templates') + +@listings_views.route('/api/log_hours', methods=['POST']) +def api_staff_log_hours(): + data = request.json + log = staff_log_hours(data['staff_username'], data['student_username'], data['hours'], data['activity']) + if not log: + return jsonify({'error': 'Failed to log hours'}), 400 + return jsonify({'message': f"Logged {log.hoursLogged} hours for student ID {log.studentID} with log ID {log.logID}"}), 201 + +@listings_views.route('/api/request_confirmation', methods=['PUT']) +def api_request_confirmation(): + data = request.json + log = request_confirmation(data['student_username'], data['activity_log_id']) + if not log: + return jsonify({'error': 'Failed to request confirmation'}), 400 + return jsonify({'message': f"Requested confirmation for log ID {log.logID}"}), 200 + +@listings_views.route('/api/leaderboard', methods=['GET']) +def api_view_leaderboard(): + leaderboard = view_leaderboard() + return jsonify(leaderboard), 200 + +@listings_views.route('/api/accolades/', methods=['GET']) +def api_view_accolades(student_username): + accolades_data = view_accolades(student_username) + if not accolades_data: + return jsonify({'error': 'Student not found'}), 404 + return jsonify(accolades_data), 200 + +@listings_views.route('/api/staff/confirm_hours', methods=['PUT']) +def api_staff_confirm_hours(): + data = request.json + log = staff_confirm_hours(data['staff_username'], data['activity_log_id']) + if not log: + return jsonify({'error': 'Failed to confirm hours'}), 400 + return jsonify({'message': f"Confirmed hours for log ID {log.logID}"}), 200 + +@listings_views.route('/api/staff/reject_hours', methods=['PUT']) +def api_staff_reject_hours(): + data = request.json + log = staff_reject_hours(data['staff_username'], data['activity_log_id']) + if not log: + return jsonify({'error': 'Failed to reject hours'}), 400 + return jsonify({'message': f"Rejected hours for log ID {log.logID}"}), 200 + +@listings_views.route('/api/update_leaderboard', methods=['PUT']) +def api_update_leaderboard(): + update_leaderboard() + return jsonify({'message': 'Leaderboard updated successfully'}), 200 + diff --git a/App/views/auth.py b/App/views/auth.py index dfc4dc9..c43c390 100644 --- a/App/views/auth.py +++ b/App/views/auth.py @@ -27,7 +27,7 @@ def identify_page(): @auth_views.route('/login', methods=['POST']) def login_action(): data = request.form - token = login(data['username'], data['password']) + token = login(data['username'], data['password'], data.get('user_type')) response = redirect(request.referrer) if not token: flash('Bad username or password given'), 401 diff --git a/App/views/user.py b/App/views/user.py index aeb6007..404d888 100644 --- a/App/views/user.py +++ b/App/views/user.py @@ -7,7 +7,9 @@ create_user, get_all_users, get_all_users_json, - jwt_required + jwt_required, + create_staff, + create_student ) user_views = Blueprint('user_views', __name__, template_folder='../templates') @@ -37,4 +39,20 @@ def create_user_endpoint(): @user_views.route('/static/users', methods=['GET']) def static_user_page(): - return send_from_directory('static', 'static-user.html') \ No newline at end of file + return send_from_directory('static', 'static-user.html') + +@user_views.route('/api/students', methods=['POST']) +def create_student_endpoint(): + data = request.json + student = create_student(data['username'], data['password']) + if student: + return jsonify({'message': f"Student {student.username} successfully created with id {student.id}"}) + return jsonify({'error': 'Failed to create student user'}), 400 + +@user_views.route('/api/staff', methods=['POST']) +def create_staff_endpoint(): + data = request.json + staff = create_staff(data['username'], data['password']) + if staff: + return jsonify({'message': f"Staff {staff.username} successfully created with id {staff.id}"}), 201 + return jsonify({'error': 'Failed to create staff user'}), 400 \ No newline at end of file