Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mysql-connector-python>=8.0.0
pymysql==1.1.1

stopit==1.1.2
setuptools
setuptools==80.10.2

pytz>=2023.3
tabulate>=0.9.0
Expand Down
3 changes: 2 additions & 1 deletion api/src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,5 @@ def custom_expired_token_response(jwt_header, jwt_payload):
# start api server
if __name__ == "__main__":
app.run(debug=isDev, host=config['HOST'], port=int(config['PORT']))



21 changes: 18 additions & 3 deletions api/src/route/api/TA/class/Assign/Exam/checkinall_POST.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from function.db import get_db
from function.isCET import isCET
from function.checkin import checkin

from flask_jwt_extended import jwt_required, get_jwt_identity

Expand Down Expand Up @@ -41,8 +40,24 @@ def main():
}), 200

try:
sql = "DELETE FROM checkout WHERE LID = %s"
cursor.execute(sql, (LID))
sql = """
INSERT IGNORE INTO checkout (UID, LID, CSYID, ip)
SELECT
STD.UID,
LB.LID,
STD.CSYID,
''
FROM
student STD
JOIN
lab LB ON LB.CSYID = STD.CSYID
WHERE
LB.LID = %s
AND (JSON_CONTAINS(LB.CID, CAST(STD.CID AS JSON))
OR JSON_CONTAINS(LB.GID, CAST(STD.GID AS JSON)));
"""
cursor.execute(sql, (LID,))
cursor.execute("DELETE FROM ticket WHERE LID = %s;", (LID,))
conn.commit()

return jsonify({
Expand Down
60 changes: 60 additions & 0 deletions api/src/route/api/TA/class/Assign/Exam/checkoutall_POST.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from flask import request, jsonify

from function.db import get_db
from function.isCET import isCET

from flask_jwt_extended import jwt_required, get_jwt_identity

@jwt_required()
def main():
Email = get_jwt_identity()['email']
conn = get_db()
cursor = conn.cursor()

form = request.get_json()
LID = form.get("LID")

query = """
SELECT
LB.CSYID
FROM
lab LB
WHERE
LB.LID = %s
"""
cursor.execute(query, (LID,))
data = cursor.fetchone()

if data is None:
return jsonify({
'success': False,
'msg': "Lab not found",
'data': {}
}), 200

if not isCET(conn, cursor, Email, data[0]):
return jsonify({
'success': False,
'msg': "You don't have permission.",
'data': {}
}), 200

try:
sql = "DELETE FROM checkout WHERE LID = %s"
cursor.execute(sql, (LID,))
cursor.execute("DELETE FROM ticket WHERE LID = %s;", (LID,))
conn.commit()

return jsonify({
'success': True,
'msg': '',
'data': {}
}), 200
except Exception as e:
print(e)
conn.rollback()
return jsonify({
'success': False,
'msg': 'Please contact admin',
'data': str(e)
}), 500
141 changes: 9 additions & 132 deletions api/src/route/api/TA/class/score_GET.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,117 +46,8 @@ def main():
}), 200


# try:
# # Get lab details
# query = """
# SELECT
# LB.CID,
# LB.GID,
# LB.Due
# FROM
# lab LB
# WHERE
# LB.LID = %s
# """
# cursor.execute(query, (LID,))
# lab_details = cursor.fetchone()
# if not lab_details:
# return jsonify({
# 'success': False,
# 'msg': 'Lab not found',
# 'data': {}
# }), 404

# CID_list = json.loads(lab_details[0]) if lab_details[0] else []
# GID_list = json.loads(lab_details[1]) if lab_details[1] else []
# due_date = lab_details[2]

# # Get questions
# query = """
# SELECT QST.QID, QST.MaxScore
# FROM question QST
# WHERE QST.LID = %s
# ORDER BY QST.QID ASC
# """
# cursor.execute(query, (LID,))
# questions = cursor.fetchall()
# if not questions:
# return jsonify({
# 'success': False,
# 'msg': 'No questions found for the lab',
# 'data': {}
# }), 404

# all_max_score = sum([int(q[1]) for q in questions])

# # Get students
# cid_condition = "ST.CID IN (%s)" % ','.join(map(str, CID_list)) if CID_list else "1=0"
# gid_condition = "ST.GID IN (%s)" % ','.join(map(str, GID_list)) if GID_list else "1=0"

# query = f"""
# SELECT ST.UID, ST.CID, ST.GID, US.Name
# FROM student ST
# JOIN user US ON ST.UID = US.UID
# WHERE {cid_condition} OR {gid_condition}
# """
# cursor.execute(query)
# students = cursor.fetchall()

# student_data = []
# for student in students:
# UID, CID, GID, Name = student
# student_smt = []
# all_score = 0

# for question in questions:
# QID, MaxScore = question

# query = """
# SELECT Score, Timestamp, SID
# FROM submitted
# WHERE UID = %s AND QID = %s AND LID = %s
# """
# cursor.execute(query, (UID, QID, LID))
# submission = cursor.fetchone()
# score = submission[0] if submission and submission[0] is not None else 0
# timestamp = submission[1] if submission else None
# SID = submission[2] if submission else -1

# if timestamp:
# timestamp_str = timestamp.strftime("%d/%m/%Y %H:%M")
# late = timestamp > due_date
# else:
# timestamp_str = "-"
# late = datetime.now() > due_date

# student_smt.append({
# "Time": timestamp_str,
# "Late": late,
# "Score": "{:.2f}".format(score),
# "MaxScore": int(MaxScore),
# "SID": SID
# })
# all_score += score

# student_data.append({
# "UID": UID,
# "Name": Name,
# "SMT": student_smt,
# "AllScore": "{:.2f}".format(all_score)
# })

# return jsonify({
# 'success': True,
# 'msg': '',
# 'data': {
# 'Students': student_data,
# 'AllMaxScore': int(all_max_score)
# }
# }), 200
# ONE massive query to fetch everything at once

try:
# 1. Get lab details
# Get lab details
query = """
SELECT
LB.CID,
Expand Down Expand Up @@ -211,25 +102,7 @@ def main():
cursor.execute(query)
students = cursor.fetchall()

# 4. OPTIMIZATION: Get all submissions for this lab in ONE query
query = """
SELECT UID, QID, Score, Timestamp, SID
FROM submitted
WHERE LID = %s
"""
cursor.execute(query, (LID,))
all_submissions = cursor.fetchall()

# Build a lookup dictionary: {(UID, QID): (Score, Timestamp, SID)}
submission_lookup = {}
for sub in all_submissions:
sub_uid, sub_qid, sub_score, sub_time, sub_sid = sub
submission_lookup[(sub_uid, sub_qid)] = (sub_score, sub_time, sub_sid)

# 5. Process data in memory (No more DB calls in the loop!)
student_data = []
current_time = datetime.now() # Call this once outside the loop for efficiency

for student in students:
UID, CID, GID, Name = student
student_smt = []
Expand All @@ -238,9 +111,13 @@ def main():
for question in questions:
QID, MaxScore = question

# Fetch from our memory dictionary instead of the database
submission = submission_lookup.get((UID, QID))

query = """
SELECT Score, Timestamp, SID
FROM submitted
WHERE UID = %s AND QID = %s AND LID = %s
"""
cursor.execute(query, (UID, QID, LID))
submission = cursor.fetchone()
score = submission[0] if submission and submission[0] is not None else 0
timestamp = submission[1] if submission else None
SID = submission[2] if submission else -1
Expand All @@ -250,7 +127,7 @@ def main():
late = timestamp > due_date
else:
timestamp_str = "-"
late = current_time > due_date
late = datetime.now() > due_date

student_smt.append({
"Time": timestamp_str,
Expand Down
50 changes: 49 additions & 1 deletion frontend/src/src/pages/PF/Checkio.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,53 @@ function Checkio() {
})
}

const checkallout = async () => {
withReactContent(Swal).fire({
title: `\nConfirm Check-Out`,
text: "Are you sure you want to check out all students?",
icon: "question",
showCloseButton: true,
showCancelButton: true,
focusConfirm: false,
confirmButtonText: `Confirm`,
confirmButtonColor: "rgb(35, 165, 85)",
}).then(async ok => {
if(ok.isConfirmed){
try{
const response = await fetch(`${host}/TA/class/Assign/Exam/checkoutall`, {
method: 'POST',
credentials: "include",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Access-Control-Allow-Origin": "*",
"X-CSRF-TOKEN": Cookies.get("csrf_token")
},
body: JSON.stringify({ LID: LID})
})
const Data = await response.json()
if (Data.success){
withReactContent(Swal).fire({
title: "Complete!",
icon: "success"
})
fetchStudent()
}else{
withReactContent(Swal).fire({
title: Data.msg,
icon: Data.data
})
}
}catch (error) {
withReactContent(Swal).fire({
title: "Please contact admin!",
text: error,
icon: "error"
})
}
}
})
}

return (
<div>
<Navbar />
Expand Down Expand Up @@ -225,8 +272,9 @@ function Checkio() {
</li>
</ul>
</div>
<div className="col-md-3">
<div className="col-auto">
<button className="btn btn-primary float-end" type="button" onClick={() => navigate("/AssignList")}>Back</button>
<button type="button" className="btn btn-secondary float-end" style={{marginRight:"1em"}} onClick={checkallout}>Check out all</button>
<button type="button" className="btn btn-secondary float-end" style={{marginRight:"1em"}} onClick={checkallin}>Check in all</button>
</div>
</div>
Expand Down
Loading