Coverage for model/course.py: 100%

147 statements  

« 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 

3 

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 

11 

12__all__ = ['course_api'] 

13 

14course_api = Blueprint('course_api', __name__) 

15 

16 

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) 

25 

26 

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.') 

52 

53 

54@course_api.route('/<course_name>', methods=['GET', 'PUT']) 

55@login_required 

56def get_course(user, course_name): 

57 course = Course(course_name) 

58 

59 if not course: 

60 return HTTPError('Course not found.', 404) 

61 

62 if not course.permission(user, Course.Permission.VIEW): 

63 return HTTPError('You are not in this course.', 403) 

64 

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) 

76 

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 

82 

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.') 

88 

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() 

101 

102 

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) 

108 

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) 

118 

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, [])]) 

128 

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.') 

143 

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.') 

164 

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.') 

176 

177 methods = { 

178 'GET': get_score, 

179 'POST': add_score, 

180 'PUT': modify_score, 

181 'DELETE': delete_score 

182 } 

183 return methods[request.method]() 

184 

185 

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) 

202 

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) 

213 

214 if not course.permission(user, Course.Permission.GRADE): 

215 return HTTPError('Permission denied', 403) 

216 

217 ret = course.get_scoreboard(pids, start, end) 

218 

219 return HTTPResponse( 

220 'Success.', 

221 data=ret, 

222 )