|
|
|
@ -1,122 +1,49 @@ |
|
|
|
|
import uuid |
|
|
|
|
|
|
|
|
|
from flask import Blueprint, request, jsonify,g |
|
|
|
|
from uuid import UUID |
|
|
|
|
from db.model import db, User, Course, Enrollment,Chat |
|
|
|
|
from ...db.model import db, User, Course, Enrollment,Chat |
|
|
|
|
from utils.auth import auth_required |
|
|
|
|
import requests |
|
|
|
|
from sqlalchemy import desc |
|
|
|
|
from config import SPAM_SCORE_THRESHOLD, AI_SPAM_SERVICES_MICROSERVICE |
|
|
|
|
from sqlalchemy import desc, select, and_ |
|
|
|
|
|
|
|
|
|
chat = Blueprint('chat', __name__) |
|
|
|
|
|
|
|
|
|
SPAM_DETECTION_URL = "http://localhost:5000/test-spam" |
|
|
|
|
|
|
|
|
|
@chat.route('/course/<uuid:course_id>/users', methods=['POST']) |
|
|
|
|
def get_users_assigned_to_course(course_id: UUID): |
|
|
|
|
""" |
|
|
|
|
Fetch all users assigned to a specific course. |
|
|
|
|
:param course_id: ID of the course to fetch users for (UUID). |
|
|
|
|
:return: JSON response with users assigned to the course. |
|
|
|
|
""" |
|
|
|
|
try: |
|
|
|
|
# Query the course to ensure it exists |
|
|
|
|
course = Course.query.get(course_id) |
|
|
|
|
if not course: |
|
|
|
|
return jsonify({"error": "Course not found."}), 404 |
|
|
|
|
|
|
|
|
|
# Get the list of users assigned to the course |
|
|
|
|
users = User.query.filter(User.enrollments.any(course_id=course_id)).all() |
|
|
|
|
|
|
|
|
|
# Prepare the response data |
|
|
|
|
user_data = [ |
|
|
|
|
{ |
|
|
|
|
"id": user.id, |
|
|
|
|
"email": user.email, |
|
|
|
|
"username": user.username, |
|
|
|
|
"firstName": user.firstName, |
|
|
|
|
"lastName": user.lastName, |
|
|
|
|
} |
|
|
|
|
for user in users |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
return jsonify({"course": course.name, "users": user_data}), 200 |
|
|
|
|
except Exception as e: |
|
|
|
|
return jsonify({"error": f"An error occurred: {str(e)}"}), 500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@chat.route('/course/<uuid:course_id>/chat', methods=['POST']) |
|
|
|
|
def join_chat(course_id: UUID): |
|
|
|
|
""" |
|
|
|
|
Allow users to join the chat only if they are enrolled in the course. |
|
|
|
|
:param course_id: ID of the course (UUID). |
|
|
|
|
:return: JSON response indicating whether the user can join or not. |
|
|
|
|
""" |
|
|
|
|
try: |
|
|
|
|
# Get the user from the request (assume user_id is passed in the request body) |
|
|
|
|
user_id = request.json.get('user_id') |
|
|
|
|
if not user_id: |
|
|
|
|
return jsonify({"error": "User ID is required."}), 400 |
|
|
|
|
|
|
|
|
|
# Query the user and ensure they exist |
|
|
|
|
user = User.query.get(user_id) |
|
|
|
|
if not user: |
|
|
|
|
return jsonify({"error": "User not found."}), 404 |
|
|
|
|
|
|
|
|
|
# Check if the user is enrolled in the course |
|
|
|
|
enrollment = Enrollment.query.filter_by(userID=user_id, courseID=course_id).first() |
|
|
|
|
if not enrollment: |
|
|
|
|
return jsonify({"error": "User is not enrolled in this course."}), 403 |
|
|
|
|
|
|
|
|
|
# If enrolled, allow the user to join the chat |
|
|
|
|
return jsonify({"message": f"User {user.username} is enrolled in the course and can join the chat."}), 200 |
|
|
|
|
except Exception as e: |
|
|
|
|
return jsonify({"error": f"An error occurred: {str(e)}"}), 500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@chat.route("/send-message", methods=["POST"]) |
|
|
|
|
@chat.route("/send", 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 |
|
|
|
|
|
|
|
|
|
current_user: User = g.current_user # Fetch the logged-in user |
|
|
|
|
data = request.args |
|
|
|
|
if not data.get("course_id"): |
|
|
|
|
return jsonify({"error": "Unknown Course"}), 400 |
|
|
|
|
if not data.get("message", "").strip(): |
|
|
|
|
return jsonify({'message': 'Silently rejected blank message'}), 200 |
|
|
|
|
try: |
|
|
|
|
# Call the spam detection service |
|
|
|
|
spam_response = requests.post(SPAM_DETECTION_URL, json={"test_message": message}) |
|
|
|
|
# Check if the user is enrolled in the course |
|
|
|
|
enrollment_record: Enrollment = db.session.execute( |
|
|
|
|
select(Enrollment).where(and_( |
|
|
|
|
Enrollment.courseID == data.get("course_id"), |
|
|
|
|
Enrollment.userID == current_user.id) |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
if not enrollment_record: |
|
|
|
|
return jsonify({"error": "You are not enrolled in this course."}), 403 |
|
|
|
|
if enrollment_record.currentPage < enrollment_record.course.pageForCommunity: |
|
|
|
|
return jsonify({ |
|
|
|
|
'message': 'You have not met the requirements to enter the chat.' |
|
|
|
|
}), 403 |
|
|
|
|
# Create and save the chat message |
|
|
|
|
spam_response = requests.post(AI_SPAM_SERVICES_MICROSERVICE, json={"test_message": data.get("message").strip()}) |
|
|
|
|
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: |
|
|
|
|
if spam_score > SPAM_SCORE_THRESHOLD: |
|
|
|
|
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, |
|
|
|
|
textContent=data.get("message").strip(), |
|
|
|
|
userID=current_user.id, |
|
|
|
|
courseID=course_id, |
|
|
|
|
courseID=data.get("course_id"), |
|
|
|
|
) |
|
|
|
|
db.session.add(new_chat) |
|
|
|
|
db.session.commit() |
|
|
|
@ -128,56 +55,71 @@ def create_chat(): |
|
|
|
|
|
|
|
|
|
@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 |
|
|
|
|
|
|
|
|
|
def get_messages(): |
|
|
|
|
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: |
|
|
|
|
course_id: uuid.UUID = uuid.UUID(request.args.get('course_id')) |
|
|
|
|
current_user: User = g.current_user |
|
|
|
|
limit = int(request.args.get('limit', 10)) |
|
|
|
|
before_id = request.args.get('before') |
|
|
|
|
after_id = request.args.get('after') |
|
|
|
|
# Verify user's enrollment |
|
|
|
|
enrollment = db.session.execute( |
|
|
|
|
select(Enrollment).where( |
|
|
|
|
and_(Enrollment.courseID == course_id, Enrollment.userID == current_user.id) |
|
|
|
|
) |
|
|
|
|
).scalar() |
|
|
|
|
if not enrollment: |
|
|
|
|
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 |
|
|
|
|
query = select(Chat).where(Chat.courseID == course_id) |
|
|
|
|
if before_id: |
|
|
|
|
try: |
|
|
|
|
reference_message: Chat = db.session.execute( |
|
|
|
|
select(Chat).where(Chat.id == uuid.UUID(before_id)) |
|
|
|
|
).scalar() |
|
|
|
|
if not reference_message: |
|
|
|
|
return jsonify({'message': 'Reference message not found'}), 404 |
|
|
|
|
query = query.order_by(Chat.chatDate.desc()).where(Chat.chatDate < reference_message.chatDate) |
|
|
|
|
except ValueError: |
|
|
|
|
return jsonify({'message': 'Invalid message ID format'}), 400 |
|
|
|
|
elif after_id: |
|
|
|
|
try: |
|
|
|
|
reference_message = db.session.execute( |
|
|
|
|
select(Chat).where(Chat.id == uuid.UUID(after_id)) |
|
|
|
|
).scalar() |
|
|
|
|
|
|
|
|
|
if not reference_message: |
|
|
|
|
return jsonify({'message': 'Reference message not found'}), 404 |
|
|
|
|
query = query.order_by(Chat.chatDate.asc()).where(Chat.chatDate > reference_message.chatDate) |
|
|
|
|
# For after queries, reverse the order later |
|
|
|
|
query = query.order_by(Chat.chatDate) |
|
|
|
|
except ValueError: |
|
|
|
|
return jsonify({'message': 'Invalid message ID format'}), 400 |
|
|
|
|
else: |
|
|
|
|
# Default ordering |
|
|
|
|
query = query.order_by(desc(Chat.chatDate)) |
|
|
|
|
|
|
|
|
|
# Apply limit and execute query |
|
|
|
|
query = query.limit(limit) |
|
|
|
|
messages = list(db.session.execute(query).scalars()) |
|
|
|
|
|
|
|
|
|
# Reverse the order for 'after' queries to maintain consistency |
|
|
|
|
if after_id: |
|
|
|
|
messages.reverse() |
|
|
|
|
|
|
|
|
|
# Format messages |
|
|
|
|
chat_messages = [{ |
|
|
|
|
'id': str(msg.id), |
|
|
|
|
'text': msg.textContent, |
|
|
|
|
'userId': str(msg.userID), |
|
|
|
|
'username': msg.user.username, |
|
|
|
|
'timestamp': msg.chatDate.isoformat() |
|
|
|
|
} for msg in messages] |
|
|
|
|
|
|
|
|
|
return jsonify({ |
|
|
|
|
'messages': chat_messages, |
|
|
|
|
'count': len(chat_messages), |
|
|
|
|
'hasMore': len(chat_messages) == limit |
|
|
|
|
}), 200 |
|
|
|
|
|
|
|
|
|
except Exception as e: |
|
|
|
|
return jsonify({"error": f"An error occurred: {str(e)}"}), 500 |
|
|
|
|
return jsonify({'message': f'An error occurred: {str(e)}'}), 500 |