Coverage for model/course.py: 100%

148 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-11-05 04:22 +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.route('/', methods=['GET', 'POST', 'PUT', 'DELETE']) 

18@login_required 

19def get_courses(user): 

20 

21 @Request.json('course', 'new_course', 'teacher') 

22 @identity_verify(0, 1) 

23 def modify_courses(user, course, new_course, teacher): 

24 r = None 

25 if user.role == 1: 

26 teacher = user.username 

27 try: 

28 if request.method == 'POST': 

29 r = Course.add_course(course, teacher) 

30 if request.method == 'PUT': 

31 co = Course(course) 

32 co.edit_course(user, new_course, teacher) 

33 if request.method == 'DELETE': 

34 co = Course(course) 

35 co.delete_course(user) 

36 except ValueError: 

37 return HTTPError('Not allowed name.', 400) 

38 except NotUniqueError: 

39 return HTTPError('Course exists.', 400) 

40 except PermissionError: 

41 return HTTPError('Forbidden.', 403) 

42 except engine.DoesNotExist as e: 

43 return HTTPError(f'{e} not found.', 404) 

44 return HTTPResponse('Success.') 

45 

46 if request.method == 'GET': 

47 data = [{ 

48 'course': c.course_name, 

49 'teacher': c.teacher.info, 

50 } for c in Course.get_user_courses(user)] 

51 return HTTPResponse('Success.', data=data) 

52 else: 

53 return modify_courses() 

54 

55 

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

57@login_required 

58def get_course(user, course_name): 

59 course = Course(course_name) 

60 

61 if not course: 

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

63 

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

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

66 

67 @Request.json('TAs', 'student_nicknames') 

68 def modify_course(TAs, student_nicknames): 

69 if not course.permission(user, Course.Permission.MODIFY): 

70 return HTTPError('Forbidden.', 403) 

71 else: 

72 tas = [] 

73 for ta in TAs: 

74 permit_user = User(ta).obj 

75 if not User(ta): 

76 return HTTPResponse(f'User: {ta} not found.', 404) 

77 tas.append(permit_user) 

78 

79 for permit_user in set(course.tas) - set(tas): 

80 course.remove_user(permit_user) 

81 for permit_user in set(tas) - set(course.tas): 

82 course.add_user(permit_user) 

83 course.tas = tas 

84 

85 try: 

86 course.update_student_namelist(student_nicknames) 

87 except engine.DoesNotExist as e: 

88 return HTTPError(str(e), 404) 

89 return HTTPResponse('Success.') 

90 

91 if request.method == 'GET': 

92 return HTTPResponse( 

93 'Success.', 

94 data={ 

95 "teacher": course.teacher.info, 

96 "TAs": [ta.info for ta in course.tas], 

97 "students": 

98 [User(name).info for name in course.student_nicknames] 

99 }, 

100 ) 

101 else: 

102 return modify_course() 

103 

104 

105@course_api.route('/<course_name>/grade/<student>', 

106 methods=['GET', 'POST', 'PUT', 'DELETE']) 

107@login_required 

108def grading(user, course_name, student): 

109 course = Course(course_name) 

110 

111 if not course: 

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

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

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

115 if student not in course.student_nicknames.keys(): 

116 return HTTPError('The student is not in the course.', 404) 

117 if course.permission(user, Course.Permission.SCORE) and \ 

118 (user.username != student or request.method != 'GET'): 

119 return HTTPError('You can only view your score.', 403) 

120 

121 def get_score(): 

122 return HTTPResponse( 

123 'Success.', 

124 data=[{ 

125 'title': score['title'], 

126 'content': score['content'], 

127 'score': score['score'], 

128 'timestamp': score['timestamp'].timestamp() 

129 } for score in course.student_scores.get(student, [])]) 

130 

131 @Request.json('title', 'content', 'score') 

132 def add_score(title, content, score): 

133 score_list = course.student_scores.get(student, []) 

134 if title in [score['title'] for score in score_list]: 

135 return HTTPError('This title is taken.', 400) 

136 score_list.append({ 

137 'title': title, 

138 'content': content, 

139 'score': score, 

140 'timestamp': datetime.now() 

141 }) 

142 course.student_scores[student] = score_list 

143 course.save() 

144 return HTTPResponse('Success.') 

145 

146 @Request.json('title', 'new_title', 'content', 'score') 

147 def modify_score(title, new_title, content, score): 

148 score_list = course.student_scores.get(student, []) 

149 title_list = [score['title'] for score in score_list] 

150 if title not in title_list: 

151 return HTTPError('Score not found.', 404) 

152 index = title_list.index(title) 

153 if new_title is not None: 

154 if new_title in title_list: 

155 return HTTPError('This title is taken.', 400) 

156 title = new_title 

157 score_list[index] = { 

158 'title': title, 

159 'content': content, 

160 'score': score, 

161 'timestamp': datetime.now() 

162 } 

163 course.student_scores[student] = score_list 

164 course.save() 

165 return HTTPResponse('Success.') 

166 

167 @Request.json('title') 

168 def delete_score(title): 

169 score_list = course.student_scores.get(student, []) 

170 title_list = [score['title'] for score in score_list] 

171 if title not in title_list: 

172 return HTTPError('Score not found.', 404) 

173 index = title_list.index(title) 

174 del score_list[index] 

175 course.student_scores[student] = score_list 

176 course.save() 

177 return HTTPResponse('Success.') 

178 

179 methods = { 

180 'GET': get_score, 

181 'POST': add_score, 

182 'PUT': modify_score, 

183 'DELETE': delete_score 

184 } 

185 return methods[request.method]() 

186 

187 

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

189@login_required 

190@Request.args('pids: str', 'start', 'end') 

191@Request.doc('course_name', 'course', Course) 

192def get_course_scoreboard( 

193 user, 

194 pids: str, 

195 start: Optional[str], 

196 end: Optional[str], 

197 course: Course, 

198): 

199 try: 

200 pids = pids.split(',') 

201 pids = [int(pid.strip()) for pid in pids] 

202 except: 

203 return HTTPError('Error occurred when parsing `pids`.', 400) 

204 

205 if start: 

206 try: 

207 start = float(start) 

208 except: 

209 return HTTPError('Type of `start` should be float.', 400) 

210 if end: 

211 try: 

212 end = float(end) 

213 except: 

214 return HTTPError('Type of `end` should be float.', 400) 

215 

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

217 return HTTPError('Permission denied', 403) 

218 

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

220 

221 return HTTPResponse( 

222 'Success.', 

223 data=ret, 

224 )