Coverage for model/course.py: 100%
147 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-14 03:01 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-14 03:01 +0000
1from typing import Optional
2from flask import Blueprint, request
4from mongo import *
5from .auth import *
6from .utils import *
7from mongo.utils import *
8from mongo.course import *
9from mongo import engine
10from datetime import datetime
12__all__ = ['course_api']
14course_api = Blueprint('course_api', __name__)
17@course_api.get('/')
18@login_required
19def get_courses(user):
20 data = [{
21 'course': c.course_name,
22 'teacher': c.teacher.info,
23 } for c in Course.get_user_courses(user)]
24 return HTTPResponse('Success.', data=data)
27@course_api.route('/', methods=['POST', 'PUT', 'DELETE'])
28@Request.json('course', 'new_course', 'teacher')
29@identity_verify(0, 1)
30def modify_courses(user, course, new_course, teacher):
31 r = None
32 if user.role == 1:
33 teacher = user.username
34 try:
35 if request.method == 'POST':
36 r = Course.add_course(course, teacher)
37 if request.method == 'PUT':
38 co = Course(course)
39 co.edit_course(user, new_course, teacher)
40 if request.method == 'DELETE':
41 co = Course(course)
42 co.delete_course(user)
43 except ValueError:
44 return HTTPError('Not allowed name.', 400)
45 except NotUniqueError:
46 return HTTPError('Course exists.', 400)
47 except PermissionError:
48 return HTTPError('Forbidden.', 403)
49 except engine.DoesNotExist as e:
50 return HTTPError(f'{e} not found.', 404)
51 return HTTPResponse('Success.')
54@course_api.route('/<course_name>', methods=['GET', 'PUT'])
55@login_required
56def get_course(user, course_name):
57 course = Course(course_name)
59 if not course:
60 return HTTPError('Course not found.', 404)
62 if not course.permission(user, Course.Permission.VIEW):
63 return HTTPError('You are not in this course.', 403)
65 @Request.json('TAs', 'student_nicknames')
66 def modify_course(TAs, student_nicknames):
67 if not course.permission(user, Course.Permission.MODIFY):
68 return HTTPError('Forbidden.', 403)
69 else:
70 tas = []
71 for ta in TAs:
72 permit_user = User(ta).obj
73 if not User(ta):
74 return HTTPResponse(f'User: {ta} not found.', 404)
75 tas.append(permit_user)
77 for permit_user in set(course.tas) - set(tas):
78 course.remove_user(permit_user)
79 for permit_user in set(tas) - set(course.tas):
80 course.add_user(permit_user)
81 course.tas = tas
83 try:
84 course.update_student_namelist(student_nicknames)
85 except engine.DoesNotExist as e:
86 return HTTPError(str(e), 404)
87 return HTTPResponse('Success.')
89 if request.method == 'GET':
90 return HTTPResponse(
91 'Success.',
92 data={
93 "teacher": course.teacher.info,
94 "TAs": [ta.info for ta in course.tas],
95 "students":
96 [User(name).info for name in course.student_nicknames]
97 },
98 )
99 else:
100 return modify_course()
103@course_api.route('/<course_name>/grade/<student>',
104 methods=['GET', 'POST', 'PUT', 'DELETE'])
105@login_required
106def grading(user, course_name, student):
107 course = Course(course_name)
109 if not course:
110 return HTTPError('Course not found.', 404)
111 if not course.permission(user, Course.Permission.VIEW):
112 return HTTPError('You are not in this course.', 403)
113 if student not in course.student_nicknames.keys():
114 return HTTPError('The student is not in the course.', 404)
115 if course.permission(user, Course.Permission.SCORE) and \
116 (user.username != student or request.method != 'GET'):
117 return HTTPError('You can only view your score.', 403)
119 def get_score():
120 return HTTPResponse(
121 'Success.',
122 data=[{
123 'title': score['title'],
124 'content': score['content'],
125 'score': score['score'],
126 'timestamp': score['timestamp'].timestamp()
127 } for score in course.student_scores.get(student, [])])
129 @Request.json('title', 'content', 'score')
130 def add_score(title, content, score):
131 score_list = course.student_scores.get(student, [])
132 if title in [score['title'] for score in score_list]:
133 return HTTPError('This title is taken.', 400)
134 score_list.append({
135 'title': title,
136 'content': content,
137 'score': score,
138 'timestamp': datetime.now()
139 })
140 course.student_scores[student] = score_list
141 course.save()
142 return HTTPResponse('Success.')
144 @Request.json('title', 'new_title', 'content', 'score')
145 def modify_score(title, new_title, content, score):
146 score_list = course.student_scores.get(student, [])
147 title_list = [score['title'] for score in score_list]
148 if title not in title_list:
149 return HTTPError('Score not found.', 404)
150 index = title_list.index(title)
151 if new_title is not None:
152 if new_title in title_list:
153 return HTTPError('This title is taken.', 400)
154 title = new_title
155 score_list[index] = {
156 'title': title,
157 'content': content,
158 'score': score,
159 'timestamp': datetime.now()
160 }
161 course.student_scores[student] = score_list
162 course.save()
163 return HTTPResponse('Success.')
165 @Request.json('title')
166 def delete_score(title):
167 score_list = course.student_scores.get(student, [])
168 title_list = [score['title'] for score in score_list]
169 if title not in title_list:
170 return HTTPError('Score not found.', 404)
171 index = title_list.index(title)
172 del score_list[index]
173 course.student_scores[student] = score_list
174 course.save()
175 return HTTPResponse('Success.')
177 methods = {
178 'GET': get_score,
179 'POST': add_score,
180 'PUT': modify_score,
181 'DELETE': delete_score
182 }
183 return methods[request.method]()
186@course_api.route('/<course_name>/scoreboard', methods=['GET'])
187@login_required
188@Request.args('pids: str', 'start', 'end')
189@Request.doc('course_name', 'course', Course)
190def get_course_scoreboard(
191 user,
192 pids: str,
193 start: Optional[str],
194 end: Optional[str],
195 course: Course,
196):
197 try:
198 pids = pids.split(',')
199 pids = [int(pid.strip()) for pid in pids]
200 except:
201 return HTTPError('Error occurred when parsing `pids`.', 400)
203 if start:
204 try:
205 start = float(start)
206 except:
207 return HTTPError('Type of `start` should be float.', 400)
208 if end:
209 try:
210 end = float(end)
211 except:
212 return HTTPError('Type of `end` should be float.', 400)
214 if not course.permission(user, Course.Permission.GRADE):
215 return HTTPError('Permission denied', 403)
217 ret = course.get_scoreboard(pids, start, end)
219 return HTTPResponse(
220 'Success.',
221 data=ret,
222 )