ayush 6 months ago
commit 057e9c9043
  1. 1
      .gitignore
  2. 14
      backend/app.py
  3. 140
      backend/blueprints/admin/__init__.py
  4. 121
      backend/blueprints/chat/__init__.py
  5. 17
      backend/blueprints/profile/__init__.py

1
.gitignore vendored

@ -26,6 +26,7 @@ env/
.venv/ .venv/
__pypackages__/ __pypackages__/
**.env
# Node.js # Node.js
node_modules/ node_modules/

@ -11,12 +11,22 @@ import uuid
from datetime import datetime from datetime import datetime
from constants import UserRole from constants import UserRole
from utils.utils import random_string_generator, hash_string from utils.utils import random_string_generator, hash_string
from flask_cors import CORS
from blueprints.profile import profile as profileBlueprint from blueprints.profile import profile as profileBlueprint
from blueprints.session import session as sessionBlueprint from blueprints.session import session as sessionBlueprint
from blueprints.admin import admin as adminBlueprint
from blueprints.chat import chat as chatBlueprint
app = Flask(__name__) app = Flask(__name__)
# Enable CORS for all routes
CORS(app)
# Enable CORS for specific routes
# CORS(app, resources={
# r"/api/*": {"origins": "*"} # Allows CORS for all `/api/` routes
# })
# Set configuration directly on the app instance # Set configuration directly on the app instance
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'} app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}
@ -26,6 +36,8 @@ db.init_app(app)
app.register_blueprint(profileBlueprint, url_prefix='/api/profile') app.register_blueprint(profileBlueprint, url_prefix='/api/profile')
app.register_blueprint(sessionBlueprint,url_prefix='/api/session') app.register_blueprint(sessionBlueprint,url_prefix='/api/session')
app.register_blueprint(adminBlueprint,url_prefix='/api/admin')
app.register_blueprint(chatBlueprint,url_prefix='/api/chat')
@app.route('/media/<string:filename>') @app.route('/media/<string:filename>')
def send_file(filename): def send_file(filename):
@ -53,7 +65,7 @@ def seed_data():
joinedDate=datetime.utcnow(), lastOnline=datetime.utcnow(), joinedDate=datetime.utcnow(), lastOnline=datetime.utcnow(),
bio=f"This is user{i}'s bio.", role=int(UserRole.USER), bio=f"This is user{i}'s bio.", role=int(UserRole.USER),
isActivated=True, sessions=[], user_badges=[], isActivated=True, sessions=[], user_badges=[],
enrollments=[], quizzes=[], quiz_attempts=[], chats=[], notifications=[]) enrollments=[], quizzes=[], quiz_attempts=[], chats=[], notifications=[],publications=[])
for i in range(1, 6) ] for i in range(1, 6) ]
db.session.add_all(users) db.session.add_all(users)
db.session.commit() db.session.commit()

@ -0,0 +1,140 @@
from utils .auth import auth_required, requires_role
from flask import Blueprint, jsonify, g
from db.model import User, Course, Enrollment, Chat, db
from sqlalchemy import select, func, desc, and_
from datetime import datetime, timedelta
from constants import UserRole
admin = Blueprint('admin', __name__)
@admin.route('/stats/users', methods=['GET'])
@auth_required()
@requires_role([UserRole.ADMIN])
def get_user_stats():
"""
Get total users and authors count.
Only accessible by admin users.
"""
try:
# Get total users
total_users = db.session.execute(
select(func.count()).select_from(User)
).scalar()
# Get authors (users who have created courses)
distinct_authors_count = db.session.execute(
select(func.count(func.distinct(Course.authorID)))
).scalar()
return jsonify({
'stats': {
'totalUsers': total_users,
'totalAuthors': distinct_authors_count
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@admin.route('/stats/enrollments', methods=['GET'])
@auth_required()
@requires_role([UserRole.ADMIN])
def get_enrollment_stats():
"""
Get course enrollment and discussion statistics.
Only accessible by admin users.
"""
try:
# Get enrollment and user counts
enrollment_stats = db.session.execute(
select(
func.count(Enrollment.id).label('total_enrollments'),
func.count(func.distinct(Enrollment.userID)).label('enrolled_users')
)
.select_from(Enrollment)
).first()
# Get course-wise enrollment counts
course_stats = db.session.execute(
select(
Course.name,
func.count(Enrollment.id).label('enrollment_count')
)
.join(Course, Course.id == Enrollment.courseID)
.group_by(Course.id)
.order_by(desc('enrollment_count'))
).all()
return jsonify({
'stats': {
'totalEnrollments': enrollment_stats.total_enrollments,
'totalEnrolledUsers': enrollment_stats.enrolled_users,
'courseEnrollments': [{
'courseName': stat.name,
'enrollmentCount': stat.enrollment_count
} for stat in course_stats]
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500
@admin.route('/stats/discussions', methods=['GET'])
@auth_required()
@requires_role([UserRole.ADMIN])
def get_discussion_stats():
"""
Get chat room activity statistics.
Only accessible by admin users.
"""
try:
# Get activity for last 24 hours
twenty_four_hours_ago = datetime.now() - timedelta(hours=24)
# Get active rooms and their stats
active_rooms = db.session.execute(
select(
Course.name,
func.count(Chat.id).label('message_count'),
func.count(func.distinct(Chat.userID)).label('active_users')
)
.join(Course, Course.id == Chat.courseID)
.where(Chat.chatDate >= twenty_four_hours_ago)
.group_by(Course.id)
.order_by(desc('message_count'))
).all()
# Get total active rooms
total_active_rooms = len(active_rooms)
# Get most active room
most_active_room = None
if active_rooms:
most_active = active_rooms[0]
most_active_room = {
'name': most_active.name,
'messageCount': most_active.message_count,
'activeUsers': most_active.active_users
}
# Get total active users across all rooms
total_active_users = db.session.execute(
select(func.count(func.distinct(Chat.userID)))
.where(Chat.chatDate >= twenty_four_hours_ago)
).scalar()
return jsonify({
'stats': {
'totalActiveRooms': total_active_rooms,
'totalActiveUsers': total_active_users,
'mostActiveRoom': most_active_room,
'activeRooms': [{
'roomName': room.name,
'messageCount': room.message_count,
'activeUsers': room.active_users
} for room in active_rooms]
}
}), 200
except Exception as e:
return jsonify({'message': f'An error occurred: {str(e)}'}), 500

@ -1,9 +1,14 @@
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify,g
from uuid import UUID from uuid import UUID
from db.model import db, User, Course, Enrollment from db.model import db, User, Course, Enrollment,Chat
from utils.auth import auth_required
import requests
from sqlalchemy import desc
chat = Blueprint('chat', __name__) chat = Blueprint('chat', __name__)
SPAM_DETECTION_URL = "http://localhost:5000/test-spam"
@chat.route('/course/<uuid:course_id>/users', methods=['POST']) @chat.route('/course/<uuid:course_id>/users', methods=['POST'])
def get_users_assigned_to_course(course_id: UUID): def get_users_assigned_to_course(course_id: UUID):
""" """
@ -64,3 +69,115 @@ def join_chat(course_id: UUID):
return jsonify({"message": f"User {user.username} is enrolled in the course and can join the chat."}), 200 return jsonify({"message": f"User {user.username} is enrolled in the course and can join the chat."}), 200
except Exception as e: except Exception as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500 return jsonify({"error": f"An error occurred: {str(e)}"}), 500
@chat.route("/send-message", methods=["POST"])
@auth_required()
def create_chat():
"""
Create a chat message for a specific course.
"""
current_user = g.current_user # Fetch the logged-in user
data = request.get_json()
# Validate the payload
course_id = data.get("course_id")
message = data.get("message")
if not course_id or not message:
return jsonify({"error": "Course ID and message are required."}), 400
try:
# Call the spam detection service
spam_response = requests.post(SPAM_DETECTION_URL, json={"test_message": message})
if spam_response.status_code != 200:
return jsonify({"error": "Failed to check message for spam."}), 500
spam_score = int(spam_response.json().get("spam_score", 0))
if spam_score > 6:
return jsonify({"error": "This message contains suspicious links or is vulnerable."}), 400
# Verify the course exists
course = db.session.query(Course).filter_by(id=course_id).first()
if not course:
return jsonify({"error": "Invalid course ID."}), 404
# Check if the user is enrolled in the course
is_enrolled = db.session.query(Course).join(Enrollment).filter(
Enrollment.userID == current_user.id,
Enrollment.courseID == course_id
).first()
if not is_enrolled:
return jsonify({"error": "You are not enrolled in this course."}), 403
# Create and save the chat message
new_chat = Chat(
textContent=message,
userID=current_user.id,
courseID=course_id,
)
db.session.add(new_chat)
db.session.commit()
return jsonify({"message": "Chat sent successfully.", "chat_id": str(new_chat.id)}), 201
except Exception as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
@chat.route("/get", methods=["GET"])
@auth_required()
def get_chat_history():
"""
Fetch chat history for a course.
"""
current_user = g.current_user # Logged-in user
course_id = request.args.get("course_id")
limit = int(request.args.get("limit", 20)) # Default to 20 messages
offset = int(request.args.get("offset", 0)) # Default to no offset (latest chats)
if not course_id:
return jsonify({"error": "Course ID is required."}), 400
try:
# Verify the course exists
course = db.session.query(Course).filter_by(id=course_id).first()
if not course:
return jsonify({"error": "Invalid course ID."}), 404
# Check if the user is enrolled in the course
is_enrolled = db.session.query(Course).join(Enrollment).filter(
Enrollment.userID == current_user.id,
Enrollment.courseID == course_id
).first()
if not is_enrolled:
return jsonify({"error": "You are not enrolled in this course."}), 403
# Fetch the latest chat messages with limit and offset
chats = (
db.session.query(Chat)
.filter_by(courseID=course_id)
.order_by(desc(Chat.chatDate))
.offset(offset)
.limit(limit)
.all()
)
# Format the chat messages
chat_history = [
{
"id": str(chat.id),
"textContent": chat.textContent,
"userID": str(chat.userID),
"chatDate": chat.chatDate.isoformat(),
"is_mine": chat.userID == current_user.id,
}
for chat in chats
]
return jsonify({"chats": chat_history}), 200
except Exception as e:
return jsonify({"error": f"An error occurred: {str(e)}"}), 500

@ -113,17 +113,17 @@ def manage_profile():
if request.method == 'GET': if request.method == 'GET':
try: try:
profile_url = url_for('send_file', filename=current_user.pfpFilename, _external=True) profile_picture = url_for('send_file', filename=current_user.pfpFilename, _external=True)
except: except:
profile_url = "" profile_picture = ""
try: try:
# Construct the user profile data # Construct the user profile data
profile_data = { profile_data = {
"id": str(current_user.id), "id": str(current_user.id),
"email": current_user.email, "email": current_user.email,
"first_name": current_user.firstName, "firstName": current_user.firstName,
"last_name": current_user.lastName, "lastName": current_user.lastName,
"username": current_user.username, "username": current_user.username,
"dob": current_user.dob.isoformat() if current_user.dob else None, "dob": current_user.dob.isoformat() if current_user.dob else None,
"joined_date": current_user.joinedDate.isoformat(), "joined_date": current_user.joinedDate.isoformat(),
@ -131,7 +131,7 @@ def manage_profile():
"bio": current_user.bio, "bio": current_user.bio,
"role": current_user.role, "role": current_user.role,
"pfp_filename": current_user.pfpFilename, "pfp_filename": current_user.pfpFilename,
"profile_url": profile_url, "profile_picture": profile_picture,
} }
return jsonify({"profile": profile_data}), 200 return jsonify({"profile": profile_data}), 200
@ -260,4 +260,9 @@ def change_password():
user.hash_password = generate_password_hash(new_password) user.hash_password = generate_password_hash(new_password)
db.session.commit() db.session.commit()
return jsonify({"message": "Password updated successfully"}), 200 return jsonify({"message": "Password updated successfully"}), 200
# @profile.route('/hello')
# @auth_required()
# @requires_role([UserRole.ADMIN])
Loading…
Cancel
Save