Skip to content

Commit

Permalink
Merge pull request #36 from CS3219-AY2425S1/jiale/session-history
Browse files Browse the repository at this point in the history
Session history implementation
  • Loading branch information
dedsecrattle authored Nov 8, 2024
2 parents b11d03b + 5beb077 commit 8c4900a
Show file tree
Hide file tree
Showing 19 changed files with 1,401 additions and 136 deletions.
2 changes: 2 additions & 0 deletions backend/collaboration-service/.env.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
LOG_LEVEL=20
USER_SERVICE_URL=http://localhost:3001
MATCHING_SERVICE_URL=http://localhost:3003

# In Docker
LOG_LEVEL=20
USER_SERVICE_URL=http://user:3001
MATCHING_SERVICE_URL=http://matching:3003
3 changes: 2 additions & 1 deletion backend/collaboration-service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
class User:
users: dict[str, "User"] = {}

def __init__(self, username: str, sid: str):
def __init__(self, user_id, username: str, sid: str):
self.user_id = user_id
self.username = username
self.cursor_position = 0
self.sid = sid
Expand Down
29 changes: 23 additions & 6 deletions backend/collaboration-service/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
import logging
import os
import dotenv
import requests

dotenv.load_dotenv()
MATCHING_SERVICE_URL = os.environ.get('MATCHING_SERVICE_URL')
if not MATCHING_SERVICE_URL:
raise ValueError('MATCHING_SERVICE_URL environment variable not set')

from events import Events
from models import Room, User
Expand All @@ -26,9 +30,11 @@ async def connect(sid, environ):
logging.debug(f"connect {token=}")
break
if token:
username = authenticate(token)
if username:
User(username, sid)
user = authenticate(token)
user_id = user.get('id', None)
username = user.get('username', None)
if user_id and username:
User(user_id, username, sid)
logging.info(f"User {username} authenticated and connected with sid {sid}")
else:
unauthenticated_sids.add(sid)
Expand Down Expand Up @@ -62,8 +68,13 @@ async def join_request(sid, data):
logging.error(f"After join_request, user.room is None for sid {sid}")
else:
logging.debug(f"User {sid} joined room {room.id}")

data = {
"user_id": user.user_id,
"room_details": room.details()
}

await sio.emit(Events.JOIN_REQUEST, room.details(), room=room_id)
await sio.emit(Events.JOIN_REQUEST, data, room=room_id)


@sio.on(Events.CODE_UPDATED)
Expand Down Expand Up @@ -167,12 +178,18 @@ async def disconnect(sid):
if room is None:
logging.error(f"User {sid} has no room during disconnect")
return

room_still_exists = room.remove_user(user)

if room_still_exists:
try:
await sio.emit(Events.USER_LEFT, user.details(), room=room.id)
logging.debug(f"Emitted USER_LEFT to room {room.id}")
except Exception as e:
logging.error(f"Failed to emit USER_LEFT for room {room.id}: {e}")
logging.error(f"Failed to emit USER_LEFT for room {room.id}: {e}")

response = requests.put(f"{MATCHING_SERVICE_URL}/leave-session", json={"data": {"roomId": room.id, "userId": user.user_id} })
if response.status_code != 200:
logging.error(f"Error communicating with matching service: {response.content}")
else:
logging.info(f"Requested matching service to mark user {user.user_id} as having left room {room.id}")
2 changes: 1 addition & 1 deletion backend/collaboration-service/user_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ def authenticate(authorization_header) -> str | None:
response = requests.get(f"{USER_SERVICE_URL}/auth/verify-token", headers={'authorization': authorization_header})
if response.status_code != 200:
return None
return response.json()["data"]['username']
return response.json()["data"]
14 changes: 14 additions & 0 deletions backend/matching-service/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,17 @@ model MatchRecord {
roomNumber String // Matched room number
questionId Int? // Question ID for the match
}

model SessionHistory {
sessionId Int @id @default(autoincrement())
roomNumber String
questionId Int
submission String?
language String?
isOngoing Boolean @default(true)
userOneId String
userTwoId String
isUserOneActive Boolean @default(true)
isUserTwoActive Boolean @default(true)
createdAt DateTime @default(now()) // Timestamp for the start of session
}
98 changes: 29 additions & 69 deletions backend/matching-service/src/matchingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ const prisma = new PrismaClient();
const DELAY_TIME = 30000;
const CONFIRM_DELAY_TIME = 10000;

export async function handleMatchingRequest(
userRequest: any,
socketId: string,
) {
export async function handleMatchingRequest(userRequest: any, socketId: string) {
userRequest.socketId = socketId;

addUserToQueue(userRequest);
Expand All @@ -37,12 +34,9 @@ function sendConfirmDelayedTimeoutMessage(recordId: string) {
recordId: recordId,
type: "confirm_timeout",
},
CONFIRM_DELAY_TIME,
);
console.log(
"Sent delayed message for confirm timeout for recordId: ",
recordId,
CONFIRM_DELAY_TIME
);
console.log("Sent delayed message for confirm timeout for recordId: ", recordId);
}

export async function handleUserRequest(userRequest: any) {
Expand All @@ -59,7 +53,7 @@ export async function handleUserRequest(userRequest: any) {
console.log("Duplicate socket detected. New socket will be used.");
io.to(pastSocketId).emit(
"duplicate socket",
"New connection detected for the same user. Please close the current page",
"New connection detected for the same user. Please close the current page"
);

// Update socket ID upon potential reconnection
Expand Down Expand Up @@ -167,20 +161,14 @@ export async function handleMatchingConfirm(userRequest: any) {
where: { recordId: matchedRecord.recordId },
data: { isArchived: true },
});
io.to(matchedRecord.socketId).emit(
"other_declined",
"Match not confirmed. Please try again.",
);
io.to(matchedRecord.socketId).emit("other_declined", "Match not confirmed. Please try again.");
}
if (userRecord !== null) {
await prisma.matchRecord.update({
where: { recordId: userRecord.recordId },
data: { isArchived: true },
});
io.to(userRecord.socketId).emit(
"other_declined",
"Match not confirmed. Please try again.",
);
io.to(userRecord.socketId).emit("other_declined", "Match not confirmed. Please try again.");
}
return;
}
Expand Down Expand Up @@ -208,24 +196,22 @@ export async function handleMatchingConfirm(userRequest: any) {
where: { recordId: userRecord.recordId },
data: { isArchived: true },
});
await prisma.sessionHistory.create({
data: {
roomNumber: matchedRecord.roomNumber,
questionId: matchedRecord.questionId ?? userRecord.questionId ?? 0,
isOngoing: true,
userOneId: userRecord.userId,
userTwoId: matchedRecord.userId,
},
});

io.to(userRecord.socketId).emit(
"matching_success",
"Match confirmed. Proceeding to collaboration service.",
);
io.to(matchedRecord.socketId).emit(
"matching_success",
"Match confirmed. Proceeding to collaboration service.",
);
io.to(userRecord.socketId).emit("matching_success", "Match confirmed. Proceeding to collaboration service.");
io.to(matchedRecord.socketId).emit("matching_success", "Match confirmed. Proceeding to collaboration service.");
// TODO: add further logic here to proceed to collaboration service
} else {
console.log(
`User ${userId} confirmed match, waiting for other user to confirm`,
);
io.to(matchedRecord.socketId).emit(
"other_accepted",
"Other user confirmed match. Please confirm.",
);
console.log(`User ${userId} confirmed match, waiting for other user to confirm`);
io.to(matchedRecord.socketId).emit("other_accepted", "Other user confirmed match. Please confirm.");
}
}

Expand All @@ -246,28 +232,16 @@ export async function handleMatchingDecline(userRequest: any) {
where: { recordId: matchedRecord.recordId },
data: { isArchived: true },
});
io.to(matchedRecord.socketId).emit(
"other_declined",
"Match not confirmed. Please try again.",
);
io.to(matchedRecord.socketId).emit(
"matching_fail",
"Match not confirmed. Please try again.",
);
io.to(matchedRecord.socketId).emit("other_declined", "Match not confirmed. Please try again.");
io.to(matchedRecord.socketId).emit("matching_fail", "Match not confirmed. Please try again.");
}
if (userRecord !== null) {
await prisma.matchRecord.update({
where: { recordId: userRecord.recordId },
data: { isArchived: true },
});
io.to(userRecord.socketId).emit(
"other_declined",
"Match not confirmed. Please try again.",
);
io.to(userRecord.socketId).emit(
"matching_fail",
"Match not confirmed. Please try again.",
);
io.to(userRecord.socketId).emit("other_declined", "Match not confirmed. Please try again.");
io.to(userRecord.socketId).emit("matching_fail", "Match not confirmed. Please try again.");
}

return;
Expand All @@ -280,23 +254,14 @@ export async function handleMatchingDecline(userRequest: any) {

// user decline, match failed regardlessly
console.log(`User ${userId} declined match`);
io.to(matchedRecord.socketId).emit(
"other_declined",
"Match not confirmed. Please try again.",
);
io.to(matchedRecord.socketId).emit("other_declined", "Match not confirmed. Please try again.");
await prisma.matchRecord.update({
where: { recordId: matchedRecord.recordId },
data: { isArchived: true },
});

io.to(userRecord.socketId).emit(
"matching_fail",
"Match not confirmed. Please try again.",
);
io.to(matchedRecord.socketId).emit(
"matching_fail",
"Match not confirmed. Please try again.",
);
io.to(userRecord.socketId).emit("matching_fail", "Match not confirmed. Please try again.");
io.to(matchedRecord.socketId).emit("matching_fail", "Match not confirmed. Please try again.");
}

export async function handleTimeout(userRequest: any) {
Expand Down Expand Up @@ -327,18 +292,13 @@ export async function handleConfirmTimeout(recordId: string) {
console.log(`Timeout: Confirm timeout for recordId ${recordId}`);
if (result !== null) {
if (result.isConfirmed === false) {
console.log(
`Timeout: Match not confirmed for recordId ${recordId} with userId ${result.userId}`,
);
console.log(`Timeout: Match not confirmed for recordId ${recordId} with userId ${result.userId}`);
} else {
console.log(
`Timeout: Match confirmed for recordId ${recordId} with userId ${result.userId} but other user did not confirm`,
`Timeout: Match confirmed for recordId ${recordId} with userId ${result.userId} but other user did not confirm`
);
}
io.to(result.socketId).emit(
"matching_fail",
"Match not confirmed. Please try again.",
);
io.to(result.socketId).emit("matching_fail", "Match not confirmed. Please try again.");
await prisma.matchRecord.update({
where: { recordId: recordIdInt },
data: { isArchived: true },
Expand Down
Loading

0 comments on commit 8c4900a

Please sign in to comment.