class NoticeListAPI(Resource): # url: /course/<int:id>/notices method_decorators = {"get": [get_course_allowed, auth_required, resource_found_required("course")], "post": [teacher_required, auth_required, resource_found_required("course")]} def get(self, cid): notices = g.current_course.notices data = Notice.list_to_json(notices) return make_resp(data) def post(self, cid): data = notice_create_reqparser.parse_args() new_notice = Notice(data['title'], data['content']) new_notice.course = g.current_course try: students = new_notice.course.students for student in students: send_notice(student, new_notice) except: pass db.session.add(new_notice) db.session.commit() return make_resp(new_notice.to_json(detail=True))
class DiscussionAPI(Resource): # url: /course/<int:cid>/discussions/<string:discus_id> method_decorators = [get_course_allowed, resource_found_required("course"), resource_found_required("discussion"), auth_required] def get(self, cid, discus_id): # get a discussion and its comments discussion = g.current_discussion return make_resp(discussion.to_json(detail=True))
class CommentLikeAPI(Resource): # url: /courses/<int:cid>/comments/<string:comment_id>/like method_decorators = [get_course_allowed, resource_found_required('course'), resource_found_required("comment"), auth_required] def post(self, cid, comment_id): if g.current_user.liked(comment_id, "comment"): return api_abort(400, "already liked") g.current_user.like(comment_id, "comment") return "OK"
class TaskListAPI(Resource): # url: /<int:cid>/tasks/?per_page=&page=&type="exam"/"test" method_decorators = {"get": [get_course_allowed, auth_required, resource_found_required('course')], "post": [teacher_required, auth_required, resource_found_required('course')]} def get(self, cid): # course = g.current_course # return make_resp(Task.list_to_json(course.tasks, g.current_user)) task_query = Task.query.filter_by(course_id=cid) task_type = request.args.get("type") if task_type is not None: task_query = task_query.filter_by(type=task_type) per_page = int(request.args.get("per_page", 20)) page = int(request.args.get("page", 1)) tasks_pagination = task_query.paginate(page, per_page=per_page, error_out=False) data = Task.page_to_json(tasks_pagination, g.current_user, cid) return make_resp(data) def post(self, cid): course = g.current_course data = task_create_reqparser.parse_args() new_task = Task(data['type'], data['name'], data['t_begin'], data['t_end'], int(data['ans_visible']), data['introduce'], data['expires']) probs = eval(data['problems']) prob_list = [] for v in probs.values(): prob_list.append(v) prob_order_set = set() for prob in prob_list: prob = prob_parser(prob) if not prob: return api_abort(400, 'bad prob param') if prob['order'] in prob_order_set: return api_abort(400, 'duplicate of order') prob_order_set.add(prob['order']) new_prob = create_prob(prob) new_task.problems.append(new_prob) new_task.judge_max_score() [new_task.students.append(student) for student in course.students] r.sadd("task_finished:"+str(new_task.id), g.current_user.id) course.tasks.append(new_task) db.session.commit() resp = new_task.to_json(g.current_user, detail=True) return make_resp(resp)
class ReplyAPI(Resource): # /course/comment/<string:comment_id>/replies method_decorators = [resource_found_required("comment"), auth_required] def get(self, comment_id): comment = g.current_comment return make_resp(comment.to_json())
class CommentAPI(Resource): # url: /course/discussions/<string:discus_id>/comments?page=&per_page= method_decorators = [resource_found_required("discussion"), auth_required] def get(self, discus_id): per_page = int(request.args.get("per_page", 20)) page = int(request.args.get("page", 1)) comments_pagination = Comment.query.filter_by(discussion_id=discus_id).paginate(page, per_page=per_page, error_out=False) data = Comment.page_to_json(comments_pagination, discus_id) return make_resp(data) def post(self, discus_id): # post a comment data = comment_reqparser.parse_args() new_comment = Comment(data['content'], data['reply']) new_comment.author = g.current_user new_comment.discussion = g.current_discussion db.session.add(new_comment) if data['reply'] is not None: comment_reply = Comment.query.get(data['reply']) if comment_reply is None: return api_abort(404, "comment reply is not exist") replies = pickle.loads(comment_reply.replies) replies.append(new_comment.id) comment_reply.replies = pickle.dumps(replies) db.session.commit() return make_resp(g.current_discussion.to_json(detail=True))
class CorrectAnswerAPI(Resource): # url: /task/answers/correct_answer?task_answer_id=<string:uuid> method_decorators = [auth_required, resource_found_required('task_answer')] def post(self): if not g.current_user.is_teacher(g.current_task_answer.task.course): return api_abort(403, "not the teacher") # 要是想以另一种形式返回也行 check_res_list = check_answer_reqparser.parse_args()['check_res'] check_res_list = eval(check_res_list) answers = dict() answer = g.current_task_answer.answers for ans in answer: answers[int(ans.order)] = ans for check_res in check_res_list: order = check_res['order'] ans = answers[order] score = check_res['score'] if check_res[ 'score'] is not None else ans.score if score > ans.problem.max_score: return api_abort( 400, "the score of answer ordered {} is too high".format(order)) ans.score = score ans.comment = check_res.get("comment", None) g.current_task_answer.judge_score() db.session.commit() return make_resp("OK")
class ProbStatisticAPI(Resource): # url: /task/<string:tid>/statistic/problems?order=<int:order>&detail=True method_decorators = [ edit_task_allowed, resource_found_required("task"), auth_required ] def get(self, tid): order = request.args.get("order", None) if order is None: return api_abort(400, "param order is needed") order = int(order) problem = None for prob in g.current_task.problems: if prob.order == order: problem = prob break if problem is None: return api_abort(400, "problem order {} is not found".format(order)) detail = request.args.get("detail", False) data = problem.statistic(detail) return make_resp(data)
class MovieUploadAPI(Resource): method_decorators = [teacher_required, auth_required, resource_found_required('course')] def post(self, cid): # expected str chapter and file document, if chapter not exist, create it. data = upload_reqparser.parse_args() chapter_name = data['chapter'] name = data['name'] chapter = Chapter.query.filter_by(name=chapter_name, course_id=g.current_course.id).first() if chapter is None: chapter = Chapter(chapter_name) chapter.course = g.current_course db.session.add(chapter) course = g.current_course movie = request.files.get('movie') if movie is None: return api_abort(400, "movie is None") new_media_uuid = Media.save_media(movie, 'course/{}/movie'.format(course.id), name=name) media_uuid_list = pickle.loads(chapter.movies) media_uuid_list.append(new_media_uuid) chapter.movies = pickle.dumps(media_uuid_list) db.session.commit() return make_resp(chapter.to_json(with_movies=True))
class MediaAPI(Resource): # url: /course/media/<string:media_id> method_decorators = [resource_found_required("media")] def get(self, media_id): media = g.current_media return make_resp(media.to_json())
class ChapterListAPI(Resource): # url: /course/<int:cid>/chapters method_decorators = [resource_found_required('course'), auth_required] def get(self, cid): chapters = g.current_course.chapters return make_resp(Chapter.list_to_json(chapters))
class TaskStuStatusAPI(Resource): # url:/task/<string:tid>/statistic/stu_status method_decorators = [ edit_task_allowed, auth_required, resource_found_required("task") ] def get(self, tid): task = g.current_task students = task.students key = "task_finished:" + str(task.id) stu_list = [] for student in students: score = 0 task_answer = set(student.answers).intersection( set(g.current_task.answers)) if task_answer: score = task_answer.pop().score if not r.sismember(key, student.id): finished = False else: finished = True data = { "name": student.name, "student_id": student.student_id, "finished": finished, "score": score } stu_list.append(data) return make_resp(stu_list)
class JoinStatusAPI(Resource): # url: /<int:cid>/join/status method_decorators = [auth_required, resource_found_required('course')] def get(self, cid): status = 1 if g.current_user in g.current_course.students else 0 return make_resp(status)
class CommitStatisticsAPI(Resource): # url: /course/<int:cid>/commit/statistics?commit_id= method_decorators = [teacher_required, resource_found_required("course"), auth_required] def get(self, cid): key = "commits:" + str(g.current_course.id) commits = r.lrange(key, 0, r.llen(key)) # find commit by commit id commit_id = request.args.get("commit_id") if commit_id is not None: status = 0 for commit in commits: if commit['id'] == commit_id: commits = [commit] status = 1 break if not status: return api_abort(404, "resource commit not found") commit_list = [] for commit in commits: commit_list.append(pickle.loads(commit)) data = { "count": len(commits), "commits": commit_list } return make_resp(data)
class MovieListAPI(Resource): # url: /course/<int:cid>/movies method_decorators = [resource_found_required('course')] def get(self, cid): course = g.current_course chapters = course.chapters data = Chapter.list_to_json(chapters, with_movies=True) return make_resp(data)
class GetStudentsAPI(Resource): # url: /<int:cid>/students method_decorators = [resource_found_required('course')] def get(self, cid): students = g.current_course.students page = request.args.get("page", 1) per_page = request.args.get("per_page", 20) students = students[(page-1)*per_page:page*per_page] data = User.list_to_json(students) return make_resp(data)
class TaskStatisticAPI(Resource): # url: /task/<string:tid>/statistic?detail=True method_decorators = [ edit_task_allowed, resource_found_required("task"), auth_required ] def get(self, tid): detail = request.args.get("detail", False) data = g.current_task.statistic(detail) return make_resp(data)
class TaskAPI(Resource): # url: /task/<int:tid> # if finished, will return the correct answer method_decorators = { "get": [get_course_allowed, auth_required, resource_found_required('task')], "delete": [edit_task_allowed, auth_required, resource_found_required('task')] } def get(self, tid): set_exam_expires(g.current_user, g.current_task) return make_resp(g.current_task.to_json(g.current_user, detail=True)) def delete(self, tid): db.session.delete(g.current_task) db.session.commit() return make_resp({})
class CourseAPI(Resource): # url: /<int:id> method_decorators = {"get": [resource_found_required('course'), auth_required], "put": [teacher_required, auth_required, resource_found_required('course')], "delete": [teacher_required, auth_required, resource_found_required('course')]} def get(self, cid): return make_resp(g.current_course.to_json(detail=True)) def put(self, cid): course = g.current_course data = course_put_reqparser.parse_args() edit_module(course, data) db.session.commit() return course.to_json(detail=True) def delete(self, cid): course = g.current_course data = course.to_json(detail=True) db.session.delete(course) db.session.commit() return make_resp(data)
class CourseAvatarAPI(Resource): # url: avatars/course/<int:cid> method_decorators = {"get": [resource_found_required('course')], "post": [auth_required, resource_found_required("course")]} def get(self, cid): url = Media.load_media_from_uuid(g.current_course.avatar, return_model=True).url return redirect(url) def post(self, cid): if not g.current_user.is_teacher(g.current_course): return api_abort(403, "permission denied") old_media = Media.load_media_from_uuid(g.current_course.avatar, return_model=True) new_media = request.files.get("avatar") if new_media is None: return api_abort(400, "file missing") new_media_uuid = Media.save_media(new_media, "avatars/course", commit=False) old_media.delete() if old_media is not None else 1 g.current_course.avatar = new_media_uuid db.session.commit() return "OK"
class JoinCourseAPI(Resource): # url: /join/<int:cid> method_decorators = [auth_required, resource_found_required('course')] def post(self, cid): course = g.current_course user = g.current_user if course in user.courses: return api_abort(400, "you already in the course") if user.is_teacher(course): return api_abort(403, "you are the teacher!") if not course.public: return api_abort(403, "can only join the public course") course.students.append(user) db.session.commit() return make_resp(course.to_json())
class ProblemAPIByOrder(Resource): # url: /task/<string:tid>/problems/<int:order> method_decorators = [ get_course_allowed, auth_required, resource_found_required('task') ] def get(self, tid, order): task = g.current_task problem = None for prob in task.problems: if prob.order == order: problem = prob break if problem is None: return api_abort(404, "problem order {} not found".format(order)) return make_resp(problem.to_json())
class DiscussionListAPI(Resource): # url: /course/<int:cid>/discussions/ method_decorators = [get_course_allowed, resource_found_required("course"), auth_required] def get(self, cid): # get discussions discussions = g.current_course.discussions return make_resp(Discussion.list_to_json(discussions)) def post(self, cid): # post a discussion data = discussion_reqparser.parse_args() new_discussion = Discussion(data['content']) new_discussion.master = g.current_user new_discussion.course = g.current_course db.session.add(new_discussion) db.session.commit() return make_resp(new_discussion.to_json(detail=True))
class ImportStuAPI(Resource): # url: course/<int:cid>/students/import method_decorators = [teacher_required, auth_required, resource_found_required('course')] def post(self, cid): course = g.current_course data = parse_excel(request, 'excel_file') # it's a list for item in data: item = deal_import_data(item) user = User.query.filter_by(school=item['school'], student_id=item['student_id']).first() if user is None: key = item['school'] + ":" + item['student_id'] item['courses'].append(course.id) r.set(key, pickle.dumps(item)) else: user.courses.append(course) user.name = item['name'] db.session.commit() return make_resp(course.to_json(detail=True))
class AnswerAPIByOrder(Resource): # url: /task/<string:tid>/answers/<int:order> method_decorators = [ get_course_allowed, auth_required, resource_found_required('task') ] def get(self, tid, order): task = g.current_task user = g.current_user task_answer = set(task.answers).intersection(set(user.answers)) if not task_answer: return api_abort(404, "you have not finished the problem") task_answer = task_answer.pop() answers = task_answer.answers answer_return = None for answer in answers: if answer.problem.order == order: answer_return = answer break if answer_return is None: return api_abort(404, "answer order {} not found".format(order)) return make_resp(answer_return.to_json())
class TaskAnswerAPI(Resource): # url: /task/<string:tid>/answers?uncheck=<bool> method_decorators = [auth_required, resource_found_required('task')] def get(self, tid): task = g.current_task user = g.current_user uncheck = request.args.get('uncheck', 0) task_answer = None if uncheck: if not user.is_teacher(task.course): return api_abort(403, 'not the teacher') for answer in task.answers: if not answer.status: task_answer = answer break if task_answer is None: return api_abort(404, "all answers checked") else: task_answer = set(user.answers).intersection(set(task.answers)) if not task_answer: return api_abort(404, "have not finished the task") task_answer = task_answer.pop() return make_resp(task_answer.to_json(detail=True))
class NoticeAPI(Resource): # url: /course/<int:cid>/notices/<string:notice_id> method_decorators = {"get": [get_course_allowed, auth_required, resource_found_required("notice"), resource_found_required("course")], "post": [get_course_allowed, auth_required, resource_found_required("notice"), resource_found_required("course")], "delete": [teacher_required, auth_required, resource_found_required("course"), resource_found_required("notice")]} def get(self, cid, notice_id): return make_resp(g.current_notice.to_json(detail=True)) def post(self, cid, notice_id): key_notice_read = "read:{}".format(notice_id) key_user_cnt = "read:{}:{}".format(cid, g.current_user.id) r.sadd(key_notice_read, g.current_user.id) r.incr(key_user_cnt) return "OK" def delete(self, cid, notice_id): db.session.delete(g.current_notice) db.session.commit() return "OK"
class AnswerSubmitAPI(Resource): # url: /task/<string:tid>/submit method_decorators = [ get_course_allowed, auth_required, resource_found_required('task') ] def post(self, tid): user = g.current_user task = g.current_task if user.is_teacher(task.course): return api_abort(403, "you are the teacher") time_now = time() if not task.time_begin <= time_now <= task.time_end: return api_abort(403, "not in the time") # delete existed answer exist_task_answer = set(task.answers).intersection(set(user.answers)) if exist_task_answer: exist_task_answer = exist_task_answer.pop() for answer in exist_task_answer.answers: if answer.media is not None: medias = Media.load_medias_from_uuid_list( pickle.loads(answer.media), return_model=True) for media in medias: media.delete() db.session.delete(exist_task_answer) new_task_answer = TaskAnswer() answers = answer_submit_reqparser.parse_args()['answers'] if not isinstance(answers, list): answers = eval(answers) problems = dict() prob_order_set = set() for prob in task.problems: problems[prob.order] = prob prob_order_set.add(prob.order) answer_order_set = set() for answer in answers: if not isinstance(answer, dict): answer = eval(answer) content = answer.get('content', None) if content == 'undefined': content = None order = answer.get("order", None) if order is None: return api_abort(400, "order is needed") answer_order_set.add(order) medias = request.files.getlist('answer' + str(order)) media_uuid_list = Media.save_medias( medias, 'answer') if len(medias) is not 0 else None new_answer = Answer(order, content, media_uuid_list) new_answer.student = user new_answer.problem = problems[order] if new_answer.problem.type is not "subjective": new_answer.judge_score() new_task_answer.answers.append(new_answer) if answer_order_set.difference(prob_order_set): return api_abort(400, "answer order over the max order") for order in prob_order_set.difference(answer_order_set): medias = request.files.getlist('answer' + str(order), None) media_uuid_list = Media.save_medias( medias, 'answer') if len(medias) is not 0 else None new_answer = Answer(order, medias=media_uuid_list) new_answer.student = user new_answer.problem = problems[order] new_task_answer.answers.append(new_answer) new_task_answer.student = user new_task_answer.task = task new_task_answer.judge_score() r.sadd("task_finished:" + task.id, user.id) db.session.add(new_task_answer) db.session.commit() data = new_task_answer.to_json(detail=True) return make_resp(data)
class ProbAPIByID(Resource): # url: /task/problem/<string:prob_id> method_decorators = [auth_required, resource_found_required('problem')] def get(self, prob_id): return make_resp(g.current_problem.to_json())
class UserAPI(Resource): # url: /user/<int:uid> method_decorators = [resource_found_required('user')] def get(self, uid): return make_resp(g.current_user.to_json(detail=False))