def post(self): """ Create new course """ require( CREATE, Course, title="Course Not Saved", message= "Sorry, your role in the system does not allow you to save courses." ) params = new_course_parser.parse_args() new_course = Course(name=params.get("name"), year=params.get("year"), term=params.get("term"), sandbox=params.get("sandbox"), start_date=params.get('start_date'), end_date=params.get('end_date', None)) if new_course.start_date is not None: new_course.start_date = datetime.datetime.strptime( new_course.start_date, '%Y-%m-%dT%H:%M:%S.%fZ') if new_course.end_date is not None: new_course.end_date = datetime.datetime.strptime( new_course.end_date, '%Y-%m-%dT%H:%M:%S.%fZ') if new_course.start_date and new_course.end_date and new_course.start_date > new_course.end_date: abort(400, title="Course Not Saved", message="Course end time must be after course start time.") try: # create the course db.session.add(new_course) # also need to enrol the user as an instructor new_user_course = UserCourse(course=new_course, user_id=current_user.id, course_role=CourseRole.instructor) db.session.add(new_user_course) db.session.commit() except exc.SQLAlchemyError as e: db.session.rollback() current_app.logger.error("Failed to add new course. " + str(e)) raise on_course_create.send(self, event_name=on_course_create.name, user=current_user, course=new_course, data=marshal(new_course, dataformat.get_course())) return marshal(new_course, dataformat.get_course())
def post(self): """ Create new course """ require(CREATE, Course, title="Course Not Saved", message="Sorry, your role in the system does not allow you to save courses.") params = new_course_parser.parse_args() new_course = Course( name=params.get("name"), year=params.get("year"), term=params.get("term"), sandbox=params.get("sandbox"), start_date=params.get('start_date'), end_date=params.get('end_date', None) ) if new_course.start_date is not None: new_course.start_date = datetime.datetime.strptime( new_course.start_date, '%Y-%m-%dT%H:%M:%S.%fZ') if new_course.end_date is not None: new_course.end_date = datetime.datetime.strptime( new_course.end_date, '%Y-%m-%dT%H:%M:%S.%fZ') if new_course.start_date and new_course.end_date and new_course.start_date > new_course.end_date: abort(400, title="Course Not Saved", message="Course end time must be after course start time.") try: # create the course db.session.add(new_course) # also need to enrol the user as an instructor new_user_course = UserCourse( course=new_course, user_id=current_user.id, course_role=CourseRole.instructor ) db.session.add(new_user_course) db.session.commit() except exc.SQLAlchemyError as e: db.session.rollback() current_app.logger.error("Failed to add new course. " + str(e)) raise on_course_create.send( self, event_name=on_course_create.name, user=current_user, course=new_course, data=marshal(new_course, dataformat.get_course())) return marshal(new_course, dataformat.get_course())
def post(self): """ Create new course """ require(CREATE, Course) params = new_course_parser.parse_args() new_course = Course( name=params.get("name"), year=params.get("year"), term=params.get("term"), description=params.get("description", None), start_date=params.get('start_date', None), end_date=params.get('end_date', None) ) if new_course.start_date is not None: new_course.start_date = datetime.datetime.strptime( new_course.start_date, '%Y-%m-%dT%H:%M:%S.%fZ') if new_course.end_date is not None: new_course.end_date = datetime.datetime.strptime( new_course.end_date, '%Y-%m-%dT%H:%M:%S.%fZ') try: # create the course db.session.add(new_course) # also need to enrol the user as an instructor new_user_course = UserCourse( course=new_course, user_id=current_user.id, course_role=CourseRole.instructor ) db.session.add(new_user_course) db.session.commit() except exc.SQLAlchemyError as e: db.session.rollback() current_app.logger.error("Failed to add new course. " + str(e)) raise on_course_create.send( self, event_name=on_course_create.name, user=current_user, course=new_course, data=marshal(new_course, dataformat.get_course())) return marshal(new_course, dataformat.get_course())
def post(self, course_uuid): """ link current session's lti context with a course """ course = Course.get_active_by_uuid_or_404(course_uuid) require(EDIT, course, title="Course Not Linked", message="Sorry, you do not have permission to link this course since you are not enrolled as an instructor in the course.") if not sess.get('LTI'): abort(400, title="Course Not Linked", message="Sorry, your LTI session has expired. Please log in via LTI and try linking again.") if not sess.get('lti_context'): abort(400, title="Course Not Linked", message="Sorry, your LTI link settings have no course context. Please edit your LTI link settings and try linking again.") lti_context = LTIContext.query.get_or_404(sess.get('lti_context')) lti_context.compair_course_id = course.id db.session.commit() # automatically fetch membership if enabled for context if lti_context.membership_enabled: update_lti_course_membership.delay(course.id) on_lti_course_link_create.send( self, event_name=on_lti_course_link_create.name, user=current_user, data={ 'course_id': course.id, 'lti_context_id': lti_context.id }) return { 'success': True }
def post(self, course_uuid, user_uuid): """ Enrol or update a user enrolment in the course The payload for the request has to contain course_role. e.g. {"couse_role":"Student"} :param course_uuid: :param user_uuid: :return: """ course = Course.get_active_by_uuid_or_404(course_uuid) user = User.get_by_uuid_or_404(user_uuid) user_course = UserCourse.query \ .filter_by( user_id=user.id, course_id=course.id ) \ .first() if not user_course: user_course = UserCourse( user_id=user.id, course_id=course.id ) require(EDIT, user_course) params = new_course_user_parser.parse_args() role_name = params.get('course_role') course_roles = [ CourseRole.dropped.value, CourseRole.student.value, CourseRole.teaching_assistant.value, CourseRole.instructor.value ] if role_name not in course_roles: abort(404) course_role = CourseRole(role_name) if user_course.course_role != course_role: user_course.course_role = course_role db.session.add(user_course) db.session.commit() result = { 'user_id': user.uuid, 'fullname': user.fullname, 'course_role': course_role.value } on_classlist_enrol.send( self, event_name=on_classlist_enrol.name, user=current_user, course_id=course.id, data={'user_id': user.id}) return result
def delete(self, course_uuid, assignment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require(DELETE, assignment, title="Assignment Not Deleted", message="Sorry, your role in this course does not allow you to delete assignments.") if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS: abort(400, title="Assignment Not Deleted", message="Sorry, you cannot remove the default demo assignments.") formatted_assignment = marshal(assignment, dataformat.get_assignment(False)) # delete file when assignment is deleted assignment.active = False assignment.clear_lti_links() db.session.commit() # update course grades course.calculate_grades() on_assignment_delete.send( self, event_name=on_assignment_delete.name, user=current_user, course_id=course.id, assignment=assignment, data=formatted_assignment) return {'id': assignment.uuid}
def post(self, course_uuid): """ refresh the course membership if able """ course = Course.get_active_by_uuid_or_404(course_uuid) require(EDIT, course) if not course.lti_linked: return {"error": "Course not linked to lti context"}, 400 try: LTIMembership.update_membership_for_course(course) except MembershipNoValidContextsException as err: return {"error": "LTI membership service is not supported for this course"}, 400 except MembershipNoResultsException as err: return {"error": "LTI membership service did not return any users"}, 400 except MembershipInvalidRequestException as err: return ( { "error": "LTI membership request was invalid. Please relaunch the ComPAIR course from the LTI consumer and try again" }, 400, ) on_lti_course_membership_update.send( self, event_name=on_lti_course_membership_update.name, user=current_user, data={"course_id": course.id} ) return {"imported": True}
def delete(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404( comparison_example_uuid) require( DELETE, comparison_example, title="Comparison Example Not Deleted", message= "Sorry, your role in this course does not allow you to delete practice answers." ) formatted_comparison_example = marshal( comparison_example, dataformat.get_comparison_example(with_answers=False)) comparison_example.active = False db.session.add(comparison_example) db.session.commit() on_comparison_example_delete.send( self, event_name=on_comparison_example_delete.name, user=current_user, course_id=course.id, data=formatted_comparison_example) return {'id': comparison_example.uuid}
def post(self, course_uuid, assignment_uuid, answer_uuid): """ Mark an answer as being a top answer :param course_uuid: :param assignment_uuid: :param answer_uuid: :return: marked answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(MANAGE, answer, title="Answer Not Added", message="Your role in this course does not allow you to add to the list of instructor-picked answers.") params = top_answer_parser.parse_args() answer.top_answer = params.get('top_answer') db.session.add(answer) on_set_top_answer.send( self, event_name=on_set_top_answer.name, user=current_user, course_id=course.id, assignment_id=assignment.id, data={'answer_id': answer.id, 'top_answer': answer.top_answer}) db.session.commit() return marshal(answer, dataformat.get_answer(restrict_user=False))
def delete(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404( assignment_comment_uuid) require( DELETE, assignment_comment, title="Help Comment Not Deleted", message= "Sorry, your role in this course does not allow you to delete help comments." ) data = marshal(assignment_comment, dataformat.get_assignment_comment(False)) assignment_comment.active = False db.session.commit() on_assignment_comment_delete.send( self, event_name=on_assignment_comment_delete.name, user=current_user, course_id=course.id, assignment_comment=assignment_comment, data=data) return {'id': assignment_comment.uuid}
def get(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require( READ, course, title="Instructors Unavailable", message= "Instructors can only be seen here by those enrolled in the course. Please double-check your enrollment in this course." ) instructors = UserCourse.query \ .filter(and_( UserCourse.course_id==course.id, UserCourse.course_role.in_([CourseRole.teaching_assistant, CourseRole.instructor]) )) \ .all() instructor_uuids = { u.user_uuid: u.course_role.value for u in instructors } on_classlist_instructor_label.send( self, event_name=on_classlist_instructor_label.name, user=current_user, course_id=course.id) return {'instructors': instructor_uuids}
def get(self, course_uuid, assignment_uuid, answer_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404( answer_uuid, joinedloads=['file', 'user', 'group', 'score']) require( READ, answer, title="Answer Unavailable", message= "Sorry, your role in this course does not allow you to view this answer." ) restrict_user = not allow(MANAGE, assignment) on_answer_get.send(self, event_name=on_answer_get.name, user=current_user, course_id=course.id, data={ 'assignment_id': assignment.id, 'answer_id': answer.id }) # don't include score/rank unless the user is non-restricted include_score = not restrict_user return marshal( answer, dataformat.get_answer(restrict_user, include_score=include_score))
def delete(self, course_uuid, user_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) user = User.get_by_uuid_or_404(user_uuid) user_course = UserCourse.query \ .filter_by( user_id=user.id, course_id=course.id ) \ .first_or_404() require(EDIT, user_course) user_course.course_role = CourseRole.dropped result = { 'user_id': user.uuid, 'fullname': user.fullname, 'course_role': CourseRole.dropped.value } db.session.add(user_course) on_classlist_unenrol.send( self, event_name=on_classlist_unenrol.name, user=current_user, course_id=course.id, data={'user_id': user.id}) db.session.commit() return result
def delete(self, course_uuid, assignment_uuid, answer_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(DELETE, answer, title="Answer Not Deleted", message="Sorry, your role in this course does not allow you to delete this answer.") if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if assignment.id in DemoDataFixture.DEFAULT_ASSIGNMENT_IDS and answer.user_id in DemoDataFixture.DEFAULT_STUDENT_IDS: abort(400, title="Answer Not Deleted", message="Sorry, you cannot delete the default student demo answers.") answer.active = False db.session.commit() # update course & assignment grade for user if answer was fully submitted if not answer.draft: if answer.user: assignment.calculate_grade(answer.user) course.calculate_grade(answer.user) elif answer.group: assignment.calculate_group_grade(answer.group) course.calculate_group_grade(answer.group) on_answer_delete.send( self, event_name=on_answer_delete.name, user=current_user, course_id=course.id, answer=answer, data={'assignment_id': assignment.id, 'answer_id': answer.id}) return {'id': answer.uuid}
def get(self, course_uuid, assignment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require( READ, assignment, title="Help Comments Unavailable", message= "Help comments can be seen only by those enrolled in the course. Please double-check your enrollment in this course." ) restrict_user = not allow(MANAGE, assignment) assignment_comments = AssignmentComment.query \ .filter_by( course_id=course.id, assignment_id=assignment.id, active=True ) \ .order_by(AssignmentComment.created.asc()).all() on_assignment_comment_list_get.send( self, event_name=on_assignment_comment_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return { "objects": marshal(assignment_comments, dataformat.get_assignment_comment(restrict_user)) }
def get(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Get an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404( answer_comment_uuid) require( READ, answer_comment, title="Reply Unavailable", message= "Sorry, your role in this course does not allow you to view this reply." ) on_answer_comment_get.send(self, event_name=on_answer_comment_get.name, user=current_user, course_id=course.id, data={ 'assignment_id': assignment.id, 'answer_id': answer.id, 'answer_comment_id': answer_comment.id }) return marshal(answer_comment, dataformat.get_answer_comment())
def post(self, course_uuid, user_uuid, group_name): course = Course.get_active_by_uuid_or_404(course_uuid) user = User.get_by_uuid_or_404(user_uuid) user_course = UserCourse.query.filter( and_( UserCourse.course_id == course.id, UserCourse.user_id == user.id, UserCourse.course_role != CourseRole.dropped, ) ).first_or_404() require(EDIT, user_course) user_course.group_name = group_name db.session.commit() on_course_group_user_create.send( current_app._get_current_object(), event_name=on_course_group_user_create.name, user=current_user, course_id=course.id, data={"user_id": user.id}, ) return {"group_name": group_name}
def get(self, course_uuid, assignment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require( READ, ComparisonExample(course_id=course.id), title="Comparison Example Unavailable", message= "Sorry, your role in this course does not allow you to view practice answers." ) # Get all comparison examples for this assignment comparison_examples = ComparisonExample.query \ .filter_by( active=True, assignment_id=assignment.id ) \ .all() on_comparison_example_list_get.send( self, event_name=on_comparison_example_list_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return { "objects": marshal(comparison_examples, dataformat.get_comparison_example()) }
def delete(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Delete an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404(answer_comment_uuid) require(DELETE, answer_comment, title="Feedback Not Deleted", message="Sorry, your role in this course does not allow you to delete feedback for this answer.") data = marshal(answer_comment, dataformat.get_answer_comment(False)) answer_comment.active = False db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_delete.send( self, event_name=on_answer_comment_delete.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=data) return {'id': answer_comment.uuid}
def get(self, course_uuid, group_name): course = Course.get_active_by_uuid_or_404(course_uuid) user_course = UserCourse(course_id=course.id) require(READ, user_course) members = User.query \ .join(UserCourse, UserCourse.user_id == User.id) \ .filter(and_( UserCourse.course_id == course.id, UserCourse.course_role != CourseRole.dropped, UserCourse.group_name == group_name )) \ .all() if len(members) == 0: abort(404) on_course_group_members_get.send( current_app._get_current_object(), event_name=on_course_group_members_get.name, user=current_user, course_id=course.id, data={'group_name': group_name}) return {'students': [{'id': u.uuid, 'name': u.fullname} for u in members]}
def post(self, course_uuid): """ link current session's lti context with a course """ course = Course.get_active_by_uuid_or_404(course_uuid) require(EDIT, course) if not sess.get("LTI"): return {"error": "Your LTI session has expired."}, 404 if not sess.get("lti_context"): return {"error": "Your LTI session has no context."}, 404 lti_context = LTIContext.query.get_or_404(sess.get("lti_context")) lti_context.compair_course_id = course.id db.session.commit() # automatically fetch membership if enabled for context if lti_context.ext_ims_lis_memberships_url and lti_context.ext_ims_lis_memberships_id: update_lti_course_membership.delay(course.id) on_lti_course_link.send( self, event_name=on_lti_course_link.name, user=current_user, data={"course_id": course.id, "lti_context_id": lti_context.id}, ) return {"success": True}
def delete(self, course_uuid, assignment_uuid, answer_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(DELETE, answer) answer.active = False if answer.file: answer.file.active = False db.session.commit() # update course & assignment grade for user if answer was fully submitted if not answer.draft: assignment.calculate_grade(answer.user) course.calculate_grade(answer.user) on_answer_delete.send( self, event_name=on_answer_delete.name, user=current_user, course_id=course.id, answer=answer, data={'assignment_id': assignment.id, 'answer_id': answer.id}) return {'id': answer.uuid}
def get(self, course_uuid): """ refresh the course membership if able """ course = Course.get_active_by_uuid_or_404(course_uuid) require(EDIT, course) if not course.lti_linked: return {"error": "Course not linked to lti context"}, 400 valid_membership_contexts = [ lti_context for lti_context in course.lti_contexts if lti_context.ext_ims_lis_memberships_url and lti_context.ext_ims_lis_memberships_id ] pending = 0 enabled = len(valid_membership_contexts) > 0 if enabled: lti_context_ids = [lti_context.id for lti_context in valid_membership_contexts] pending = ( LTIMembership.query.join(LTIUser) .filter(and_(LTIUser.compair_user_id == None, LTIMembership.lti_context_id.in_(lti_context_ids))) .count() ) status = {"enabled": enabled, "pending": pending} on_lti_course_membership_status_get.send( self, event_name=on_lti_course_membership_status_get.name, user=current_user, data={"course_id": course.id} ) return {"status": status}
def post(self, course_uuid, assignment_uuid, answer_uuid): """ Mark an answer as being a top answer :param course_uuid: :param assignment_uuid: :param answer_uuid: :return: marked answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(MANAGE, answer) params = top_answer_parser.parse_args() answer.top_answer = params.get('top_answer') db.session.add(answer) on_set_top_answer.send( self, event_name=on_set_top_answer.name, user=current_user, course_id=course.id, assignment_id=assignment.id, data={'answer_id': answer.id, 'top_answer': answer.top_answer}) db.session.commit() return marshal(answer, dataformat.get_answer(restrict_user=False))
def delete(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Delete an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404(answer_comment_uuid) require(DELETE, answer_comment) data = marshal(answer_comment, dataformat.get_answer_comment(False)) answer_comment.active = False db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_delete.send( self, event_name=on_answer_comment_delete.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=data, ) return {"id": answer_comment.uuid}
def delete(self, course_uuid, assignment_uuid, answer_uuid, answer_comment_uuid): """ Delete an answer comment """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer_comment = AnswerComment.get_active_by_uuid_or_404( answer_comment_uuid) require( DELETE, answer_comment, title="Reply Not Deleted", message= "Sorry, your role in this course does not allow you to delete replies for this answer." ) data = marshal(answer_comment, dataformat.get_answer_comment(False)) answer_comment.active = False db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_delete.send(self, event_name=on_answer_comment_delete.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=data) return {'id': answer_comment.uuid}
def post(self, course_uuid): """ refresh the course membership if able """ course = Course.get_active_by_uuid_or_404(course_uuid) require(EDIT, course, title="Membership Not Updated", message="Sorry, your role in this course does not allow you to update membership.") if not course.lti_linked: abort(400, title="Membership Not Updated", message="Sorry, your LTI link settings have no course context. Please edit your LTI link settings and try linking again.") try: LTIMembership.update_membership_for_course(course) except MembershipNoValidContextsException as err: abort(400, title="Membership Not Updated", message="The LTI link does not support the membership extension. Please edit your LTI link settings or contact your system administrator and try again.") except MembershipNoResultsException as err: abort(400, title="Membership Not Updated", message="The membership service did not return any users. Please check your LTI course and try again.") except MembershipInvalidRequestException as err: abort(400, title="Membership Not Updated", message="The membership request was invalid. Please relaunch the LTI link and try again.") on_lti_course_membership_update.send( self, event_name=on_lti_course_membership_update.name, user=current_user, data={ 'course_id': course.id }) return { 'imported': True }
def get(self, course_uuid, group_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) group = Group.get_active_by_uuid_or_404(group_uuid) require( READ, UserCourse(course_id=course.id), title="Group Members Unavailable", message= "Group membership can be seen only by those enrolled in the course. Please double-check your enrollment in this course." ) members = User.query \ .join(UserCourse, UserCourse.user_id == User.id) \ .filter(and_( UserCourse.course_id == course.id, UserCourse.course_role != CourseRole.dropped, UserCourse.group_id == group.id )) \ .order_by(User.lastname, User.firstname) \ .all() on_group_user_list_get.send(current_app._get_current_object(), event_name=on_group_user_list_get.name, user=current_user, course_id=course.id, data={'group_id': group.id}) return { 'objects': [{ 'id': u.uuid, 'name': u.fullname_sortable } for u in members] }
def delete(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require( DELETE, course, title="Course Not Deleted", message= "Sorry, your role in this course does not allow you to delete it.") if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if course.id == DemoDataFixture.DEFAULT_COURSE_ID: abort( 400, title="Course Not Deleted", message="Sorry, you cannot remove the default demo course." ) course.active = False course.clear_lti_links() db.session.commit() on_course_delete.send(self, event_name=on_course_delete.name, user=current_user, course=course, data={'id': course.id}) return {'id': course.uuid}
def delete(self, course_uuid, lti_context_uuid): """ unlink lti context from course """ course = Course.get_active_by_uuid_or_404(course_uuid) lti_context = LTIContext.get_by_uuid_or_404(lti_context_uuid) require(DELETE, lti_context, title="Course Not Unlinked", message="Sorry, your system role does not allow you to unlink LTI courses.") if lti_context.compair_course_id != course.id: abort(400, title="Course Not Unlinked", message="Sorry, The LTI context is already not linked to the course.") lti_context.compair_course_id = None db.session.commit() # automatically refresh membership if it was enabled for the removed context if lti_context.membership_enabled: update_lti_course_membership.delay(course.id) on_lti_course_unlink.send( self, event_name=on_lti_course_unlink.name, user=current_user, data={ 'course_id': course.id, 'lti_context_id': lti_context.id }) return { 'success': True }
def post(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404(comparison_example_uuid) require(EDIT, comparison_example) params = existing_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True comparison_example.answer1 = answer1 else: return {"error": "Comparison examples must have 2 answers"}, 400 if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True comparison_example.answer2 = answer2 else: return {"error": "Comparison examples must have 2 answers"}, 400 on_comparison_example_modified.send( self, event_name=on_comparison_example_modified.name, user=current_user, course_id=course.id, data=get_model_changes(comparison_example)) db.session.add(comparison_example) db.session.commit() return marshal(comparison_example, dataformat.get_comparison_example())
def post(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require( EDIT, course, title="Course Not Saved", message= "Sorry, your role in this course does not allow you to save changes to it." ) if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if course.id == DemoDataFixture.DEFAULT_COURSE_ID: abort( 400, title="Course Not Updated", message="Sorry, you cannot edit the default demo course.") params = existing_course_parser.parse_args() # make sure the course id in the url and the course id in the params match if params['id'] != course_uuid: abort( 400, title="Course Not Saved", message= "The course's ID does not match the URL, which is required in order to save the course." ) # modify course according to new values, preserve original values if values not passed course.name = params.get("name", course.name) course.year = params.get("year", course.year) course.term = params.get("term", course.term) course.sandbox = params.get("sandbox", course.sandbox) course.start_date = params.get("start_date") if course.start_date is not None: course.start_date = datetime.datetime.strptime( course.start_date, '%Y-%m-%dT%H:%M:%S.%fZ') course.end_date = params.get("end_date", None) if course.end_date is not None: course.end_date = datetime.datetime.strptime( course.end_date, '%Y-%m-%dT%H:%M:%S.%fZ') if course.start_date and course.end_date and course.start_date > course.end_date: abort(400, title="Course Not Saved", message="Course end time must be after course start time.") model_changes = get_model_changes(course) db.session.commit() on_course_modified.send(self, event_name=on_course_modified.name, user=current_user, course=course, data=model_changes) return marshal(course, dataformat.get_course())
def post(self, course_uuid, assignment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require(CREATE, AssignmentComment(course_id=course.id)) new_assignment_comment = AssignmentComment(assignment_id=assignment.id) params = new_assignment_comment_parser.parse_args() new_assignment_comment.content = params.get("content") if not new_assignment_comment.content: return {"error": "The comment content is empty!"}, 400 new_assignment_comment.user_id = current_user.id db.session.add(new_assignment_comment) db.session.commit() on_assignment_comment_create.send( self, event_name=on_assignment_comment_create.name, user=current_user, course_id=course.id, assignment_comment=new_assignment_comment, data=marshal(new_assignment_comment, dataformat.get_assignment_comment(False))) return marshal(new_assignment_comment, dataformat.get_assignment_comment())
def post(self, course_uuid, assignment_uuid, assignment_comment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) assignment_comment = AssignmentComment.get_active_by_uuid_or_404(assignment_comment_uuid) require(EDIT, assignment_comment) params = existing_assignment_comment_parser.parse_args() # make sure the comment id in the rul and the id matches if params['id'] != assignment_comment_uuid: return {"error": "Comment id does not match URL."}, 400 # modify comment according to new values, preserve original values if values not passed if not params.get("content"): return {"error": "The comment content is empty!"}, 400 assignment_comment.content = params.get("content") db.session.add(assignment_comment) on_assignment_comment_modified.send( self, event_name=on_assignment_comment_modified.name, user=current_user, course_id=course.id, assignment_comment=assignment_comment, data=get_model_changes(assignment_comment)) db.session.commit() return marshal(assignment_comment, dataformat.get_assignment_comment())
def delete(self, course_uuid, user_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) user = User.get_by_uuid_or_404(user_uuid) user_course = UserCourse.query \ .filter_by( course_id=course.id, user_id=user.id ) \ .first_or_404() require( EDIT, user_course, title="Group Not Saved", message= "Sorry, your role in this course does not allow you to save groups." ) user_course.group_name = None db.session.commit() on_course_group_user_delete.send( current_app._get_current_object(), event_name=on_course_group_user_delete.name, user=current_user, course_id=course.id, data={'user_id': user.id}) return {'user_id': user.uuid, 'course_id': course.uuid}
def post(self, course_uuid, user_uuid, group_name): course = Course.get_active_by_uuid_or_404(course_uuid) user = User.get_by_uuid_or_404(user_uuid) user_course = UserCourse.query \ .filter(and_( UserCourse.course_id == course.id, UserCourse.user_id == user.id, UserCourse.course_role != CourseRole.dropped )) \ .first_or_404() require( EDIT, user_course, title="Group Not Saved", message= "Sorry, your role in this course does not allow you to save groups." ) user_course.group_name = group_name db.session.commit() on_course_group_user_create.send( current_app._get_current_object(), event_name=on_course_group_user_create.name, user=current_user, course_id=course.id, data={'user_id': user.id}) return {'group_name': group_name}
def get(self, course_uuid, assignment_uuid): # course unused, but we need to call it to check if it's a valid course course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) # permission checks require( MANAGE, assignment, title="Unable to download attachments", message= "Sorry, your system role does not allow downloading all attachments" ) # grab answers so we can see how many has files answers = self.getStudentAnswersByAssignment(assignment) fileIds = [] fileAuthors = {} for answer in answers: if not answer.file_id: continue # answer has an attachment fileIds.append(answer.file_id) # the user who uploaded the file can be different from the answer # author (e.g. instructor can upload on behalf of student), so # we need to use the answer author instead of file uploader author = answer.user_fullname if answer.user_student_number: author += self.DELIM + answer.user_student_number fileAuthors[answer.file_id] = author if not fileIds: return {'msg': 'Assignment has no attachments'} # grab files so we can get the real file location files = self.getFilesByIds(fileIds) # zip up the tmp dir and save it to the report dir zipName = '{} [attachments-{}].zip'.format(assignment.name, assignment.uuid) zipPath = '{}/{}'.format(current_app.config['REPORT_FOLDER'], zipName) # we're using compression level 6 as a compromise between speed & # compression (this is Z_DEFAULT_COMPRESSION in zlib) with zipfile.ZipFile(zipPath, 'w', zipfile.ZIP_DEFLATED, True, 6) as zipFile: for srcFile in files: srcFilePath = '{}/{}'.format( current_app.config['ATTACHMENT_UPLOAD_FOLDER'], srcFile.name) # filename should be 'full name - student number - uuid.ext' # student number is omitted if user doesn't have one srcFileName = fileAuthors[ srcFile.id] + self.DELIM + srcFile.name #current_app.logger.debug("writing " + srcFileName) zipFile.write(srcFilePath, srcFileName) #current_app.logger.debug("Writing zip file") return {'file': 'report/' + zipName, 'filename': zipName}
def get(self, course_uuid, assignment_uuid): """ Get answers submitted to the assignment submitted by current user :param course_uuid: :param assignment_uuid: :return: answers """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require( READ, Answer(user_id=current_user.id), title="Answers Unavailable", message= "Sorry, your role in this course does not allow you to view answers for this assignment." ) restrict_user = not allow(MANAGE, assignment) params = user_answer_list_parser.parse_args() query = Answer.query \ .options(joinedload('comments')) \ .options(joinedload('file')) \ .options(joinedload('user')) \ .options(joinedload('group')) \ .options(joinedload('score')) \ .filter_by( active=True, assignment_id=assignment.id, course_id=course.id, draft=params.get('draft') ) # get group and individual answers for user if applicable group = current_user.get_course_group(course.id) if group: query = query.filter( or_(Answer.user_id == current_user.id, Answer.group_id == group.id)) # get just individual answers for user else: query = query.filter(Answer.user_id == current_user.id) answers = query.all() on_user_answer_get.send(self, event_name=on_user_answer_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return { "objects": marshal(answers, dataformat.get_answer(restrict_user)) }
def get(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require(READ, course) on_course_get.send( self, event_name=on_course_get.name, user=current_user, data={'id': course.id}) return marshal(course, dataformat.get_course())
def post(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require(EDIT, UserCourse(course_id=course.id)) params = update_users_course_role_parser.parse_args() role_name = params.get('course_role') course_roles = [ CourseRole.dropped.value, CourseRole.student.value, CourseRole.teaching_assistant.value, CourseRole.instructor.value ] if role_name not in course_roles: abort(404) course_role = CourseRole(role_name) if len(params.get('ids')) == 0: return {"error": "Please select at least one user below"}, 400 user_courses = UserCourse.query \ .join(User, UserCourse.user_id == User.id) \ .filter(and_( UserCourse.course_id == course.id, User.uuid.in_(params.get('ids')), UserCourse.course_role != CourseRole.dropped )) \ .all() if len(params.get('ids')) != len(user_courses): return {"error": "One or more users are not enrolled in the course"}, 400 if len(user_courses) == 1 and user_courses[0].user_id == current_user.id: if course_role == CourseRole.dropped: return {"error": "You cannot drop yourself from the course. Please select other users"}, 400 else: return {"error": "You cannot change your own course role. Please select other users"}, 400 for user_course in user_courses: # skip current user if user_course.user_id == current_user.id: continue # update user's role' user_course.course_role = course_role db.session.commit() on_classlist_update_users_course_roles.send( current_app._get_current_object(), event_name=on_classlist_update_users_course_roles.name, user=current_user, course_id=course.id, data={'user_uuids': params.get('ids'), 'course_role': role_name}) return {'course_role': role_name}
def post(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) user_course = UserCourse(course_id=course.id) require(EDIT, user_course, title="Class List Not Imported", message="Sorry, your role in this course does not allow you to import or otherwise change the class list.") if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if course.id == DemoDataFixture.DEFAULT_COURSE_ID: abort(400, title="Class List Not Imported", message="Sorry, you cannot import users for the default demo course.") params = import_classlist_parser.parse_args() import_type = params.get('import_type') if import_type not in [ThirdPartyType.cas.value, ThirdPartyType.saml.value, None]: abort(400, title="Class List Not Imported", message="Please select a way for students to log in and try importing again.") elif import_type == ThirdPartyType.cas.value and not current_app.config.get('CAS_LOGIN_ENABLED'): abort(400, title="Class List Not Imported", message="Please select another way for students to log in and try importing again. Students are not able to use CWL logins based on the current settings.") elif import_type == ThirdPartyType.saml.value and not current_app.config.get('SAML_LOGIN_ENABLED'): abort(400, title="Class List Not Imported", message="Please select another way for students to log in and try importing again. Students are not able to use CWL logins based on the current settings.") elif import_type is None and not current_app.config.get('APP_LOGIN_ENABLED'): abort(400, title="Class List Not Imported", message="Please select another way for students to log in and try importing again. Students are not able to use the ComPAIR logins based on the current settings.") uploaded_file = request.files['file'] results = {'success': 0, 'invalids': []} if not uploaded_file: abort(400, title="Class List Not Imported", message="No file was found to upload. Please try uploading again.") elif not allowed_file(uploaded_file.filename, current_app.config['UPLOAD_ALLOWED_EXTENSIONS']): abort(400, title="Class List Not Imported", message="Sorry, only CSV files can be imported. Please try again with a CSV file.") unique = str(uuid.uuid4()) filename = unique + secure_filename(uploaded_file.filename) tmp_name = os.path.join(current_app.config['UPLOAD_FOLDER'], filename) uploaded_file.save(tmp_name) current_app.logger.debug("Importing for course " + str(course.id) + " with " + filename) with open(tmp_name, 'rb') as csvfile: spamreader = csv.reader(csvfile) users = [] for row in spamreader: if row: users.append(row) if len(users) > 0: results = import_users(import_type, course, users) on_classlist_upload.send( self, event_name=on_classlist_upload.name, user=current_user, course_id=course.id) os.remove(tmp_name) current_app.logger.debug("Class Import for course " + str(course.id) + " is successful. Removed file.") return results
def post(self, course_uuid, assignment_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require( CREATE, ComparisonExample(assignment=Assignment(course_id=course.id)), title="Comparison Example Not Saved", message= "Sorry, your role in this course does not allow you to save practice answers." ) new_comparison_example = ComparisonExample(assignment_id=assignment.id) params = new_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True new_comparison_example.answer1 = answer1 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True new_comparison_example.answer2 = answer2 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) on_comparison_example_create.send( self, event_name=on_comparison_example_create.name, user=current_user, course_id=course.id, data=marshal( new_comparison_example, dataformat.get_comparison_example(with_answers=False))) db.session.add(new_comparison_example) db.session.commit() return marshal(new_comparison_example, dataformat.get_comparison_example())
def post(self, course_uuid, assignment_uuid, answer_uuid): """ Create comment for an answer """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) answer = Answer.get_active_by_uuid_or_404(answer_uuid) require(CREATE, AnswerComment(course_id=course.id)) answer_comment = AnswerComment(answer_id=answer.id) params = new_answer_comment_parser.parse_args() answer_comment.draft = params.get("draft") answer_comment.content = params.get("content") # require content not empty if not a draft if not answer_comment.content and not answer_comment.draft: return {"error": "The comment content is empty!"}, 400 if params.get("user_id") and current_user.system_role == SystemRole.sys_admin: user = User.get_by_uuid_or_404(params.get("user_id")) answer_comment.user_id = user.id else: answer_comment.user_id = current_user.id comment_types = [ AnswerCommentType.public.value, AnswerCommentType.private.value, AnswerCommentType.evaluation.value, AnswerCommentType.self_evaluation.value, ] comment_type = params.get("comment_type") if comment_type not in comment_types: abort(400) answer_comment.comment_type = AnswerCommentType(comment_type) db.session.add(answer_comment) db.session.commit() # update course & assignment grade for user if self-evaluation is completed if not answer_comment.draft and answer_comment.comment_type == AnswerCommentType.self_evaluation: assignment.calculate_grade(answer_comment.user) course.calculate_grade(answer_comment.user) on_answer_comment_create.send( self, event_name=on_answer_comment_create.name, user=current_user, course_id=course.id, answer_comment=answer_comment, data=marshal(answer_comment, dataformat.get_answer_comment(False)), ) return marshal(answer_comment, dataformat.get_answer_comment())
def get(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require(READ, course, title="Course Unavailable", message="Courses can be seen only by those enrolled in them. Please double-check your enrollment in this course.") on_course_get.send( self, event_name=on_course_get.name, user=current_user, data={'id': course.id}) return marshal(course, dataformat.get_course())
def post(self, course_uuid, assignment_uuid, comparison_example_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) comparison_example = ComparisonExample.get_active_by_uuid_or_404( comparison_example_uuid) require( EDIT, comparison_example, title="Comparison Example Not Saved", message= "Sorry, your role in this course does not allow you to save practice answers." ) params = existing_comparison_example_parser.parse_args() answer1_uuid = params.get("answer1_id") answer2_uuid = params.get("answer2_id") if answer1_uuid: answer1 = Answer.get_active_by_uuid_or_404(answer1_uuid) answer1.practice = True comparison_example.answer1 = answer1 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) if answer2_uuid: answer2 = Answer.get_active_by_uuid_or_404(answer2_uuid) answer2.practice = True comparison_example.answer2 = answer2 else: abort( 400, title="Comparison Example Not Saved", message= "Please add two answers with content to the practice answers and try again." ) model_changes = get_model_changes(comparison_example) db.session.add(comparison_example) db.session.commit() on_comparison_example_modified.send( self, event_name=on_comparison_example_modified.name, user=current_user, course_id=course.id, data=model_changes) return marshal(comparison_example, dataformat.get_comparison_example())
def get(self, course_uuid, assignment_uuid): # course unused, but we need to call it to check if it's a valid course course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) # permission checks require( MANAGE, assignment, title="Unable to download attachments", message= "Sorry, your system role does not allow downloading all attachments" ) # grab answers so we can see how many has files answers = self.getAnswersByAssignment(assignment) fileIds = [] for answer in answers: if not answer.file_id: continue # answer has an attachment fileIds.append(answer.file_id) if not fileIds: return {'msg': 'Assignment has no attachments'} # grab files so we can get the real file location files = self.getFilesByIds(fileIds) # zip up the tmp dir and save it to the report dir zipName = '{} [attachments-{}].zip'.format(assignment.name, assignment.uuid) zipPath = '{}/{}'.format(current_app.config['REPORT_FOLDER'], zipName) # we're using compression level 6 as a compromise between speed & # compression (this is Z_DEFAULT_COMPRESSION in zlib) with zipfile.ZipFile(zipPath, 'w', zipfile.ZIP_DEFLATED, True, 6) as zipFile: for srcFile in files: srcFilePath = '{}/{}'.format( current_app.config['ATTACHMENT_UPLOAD_FOLDER'], srcFile.name) # set filename to 'full name - student number - uuid.ext' # omit student number or extension if not exist delim = ' - ' srcFileName = srcFile.user.fullname if srcFile.user.student_number: srcFileName += delim + srcFile.user.student_number srcFileName += delim + srcFile.name #current_app.logger.debug("writing " + srcFileName) zipFile.write(srcFilePath, srcFileName) #current_app.logger.debug("Writing zip file") return {'file': 'report/' + zipName, 'filename': zipName}
def get(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require(READ, UserCourse(course_id=course.id)) restrict_user = not allow(READ, USER_IDENTITY) # expire current_user from the session. When loading classlist from database, if the # user is already in the session, e.g. instructor for the course, the User.user_courses # is not loaded from the query below, but from session. In this case, if user has more # than one course, User.user_courses will return multiple row. Thus violating the # course_role constrain. db.session.expire(current_user) users = User.query \ .join(UserCourse, UserCourse.user_id == User.id) \ .add_columns(UserCourse.course_role, UserCourse.group_name) \ .filter(and_( UserCourse.course_id == course.id, UserCourse.course_role != CourseRole.dropped )) \ .order_by(User.firstname) \ .all() if not restrict_user: user_ids = [_user.id for (_user, _course_role, _group_name) in users] third_party_auths = ThirdPartyUser.query \ .filter(and_( ThirdPartyUser.user_id.in_(user_ids), ThirdPartyUser.third_party_type == ThirdPartyType.cas )) \ .all() class_list = [] for (_user, _course_role, _group_name) in users: _user.course_role = _course_role _user.group_name = _group_name if not restrict_user: third_party_auth = next( (third_party_auth for third_party_auth in third_party_auths if third_party_auth.user_id == _user.id), None ) _user.cas_username = third_party_auth.unique_identifier if third_party_auth else None class_list.append(_user) on_classlist_get.send( self, event_name=on_classlist_get.name, user=current_user, course_id=course.id) return {'objects': marshal(class_list, dataformat.get_users_in_course(restrict_user=restrict_user))}
def get(self, course_uuid, assignment_uuid): """ Get answers submitted to the assignment submitted by current user :param course_uuid: :param assignment_uuid: :return: answers """ course = Course.get_active_by_uuid_or_404(course_uuid) assignment = Assignment.get_active_by_uuid_or_404(assignment_uuid) require(READ, Answer(user_id=current_user.id), title="Answers Unavailable", message="Sorry, your role in this course does not allow you to view answers for this assignment.") restrict_user = not allow(MANAGE, assignment) params = user_answer_list_parser.parse_args() query = Answer.query \ .options(joinedload('comments')) \ .options(joinedload('file')) \ .options(joinedload('user')) \ .options(joinedload('group')) \ .options(joinedload('score')) \ .filter_by( active=True, assignment_id=assignment.id, course_id=course.id, draft=params.get('draft') ) # get group and individual answers for user if applicable group = current_user.get_course_group(course.id) if group: query = query.filter(or_( Answer.user_id == current_user.id, Answer.group_id == group.id )) # get just individual answers for user else: query = query.filter(Answer.user_id == current_user.id) answers = query.all() on_user_answer_get.send( self, event_name=on_user_answer_get.name, user=current_user, course_id=course.id, data={'assignment_id': assignment.id}) return {"objects": marshal(answers, dataformat.get_answer(restrict_user))}
def get(self, course_uuid): """ refresh the course membership if able """ course = Course.get_active_by_uuid_or_404(course_uuid) require( EDIT, course, title="Membership Status Unavailable", message= "Sorry, your role in this course does not allow you to view LTI membership status." ) if not course.lti_linked: abort( 400, title="Membership Status Unavailable", message= "The course is not linked to an LTI context yet. Launch an LTI link to link this course first, then check the status." ) valid_membership_contexts = [ lti_context for lti_context in course.lti_contexts \ if lti_context.membership_enabled ] pending = 0 enabled = len(valid_membership_contexts) > 0 if enabled: lti_context_ids = [ lti_context.id for lti_context in valid_membership_contexts ] pending = LTIMembership.query \ .join(LTIUser) \ .filter(and_( LTIUser.compair_user_id == None, LTIMembership.lti_context_id.in_(lti_context_ids) )) \ .count() status = {'enabled': enabled, 'pending': pending} on_lti_course_membership_status_get.send( self, event_name=on_lti_course_membership_status_get.name, user=current_user, data={'course_id': course.id}) return {'status': status}
def post(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require( EDIT, UserCourse(course_id=course.id), title="Group Not Saved", message= "Sorry, your role in this course does not allow you to save groups." ) params = user_list_parser.parse_args() if len(params.get('ids')) == 0: abort( 400, title="Group Not Saved", message= "Please select at least one user below and then try to apply the group again." ) user_courses = UserCourse.query \ .join(User, UserCourse.user_id == User.id) \ .filter(and_( UserCourse.course_id == course.id, User.uuid.in_(params.get('ids')), UserCourse.course_role != CourseRole.dropped )) \ .all() if len(params.get('ids')) != len(user_courses): abort( 400, title="Group Not Saved", message= "One or more users selected are not enrolled in the course yet." ) for user_course in user_courses: user_course.group_name = None db.session.commit() on_course_group_user_list_delete.send( current_app._get_current_object(), event_name=on_course_group_user_list_delete.name, user=current_user, course_id=course.id, data={'user_uuids': params.get('ids')}) return {'course_id': course.uuid}
def post(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require(EDIT, course, title="Course Not Saved", message="Sorry, your role in this course does not allow you to save changes to it.") if current_app.config.get('DEMO_INSTALLATION', False): from data.fixtures import DemoDataFixture if course.id == DemoDataFixture.DEFAULT_COURSE_ID: abort(400, title="Course Not Updated", message="Sorry, you cannot edit the default demo course.") params = existing_course_parser.parse_args() # make sure the course id in the url and the course id in the params match if params['id'] != course_uuid: abort(400, title="Course Not Saved", message="The course's ID does not match the URL, which is required in order to save the course.") # modify course according to new values, preserve original values if values not passed course.name = params.get("name", course.name) course.year = params.get("year", course.year) course.term = params.get("term", course.term) course.sandbox = params.get("sandbox", course.sandbox) course.start_date = params.get("start_date") if course.start_date is not None: course.start_date = datetime.datetime.strptime( course.start_date, '%Y-%m-%dT%H:%M:%S.%fZ') course.end_date = params.get("end_date", None) if course.end_date is not None: course.end_date = datetime.datetime.strptime( course.end_date, '%Y-%m-%dT%H:%M:%S.%fZ') if course.start_date and course.end_date and course.start_date > course.end_date: abort(400, title="Course Not Saved", message="Course end time must be after course start time.") model_changes = get_model_changes(course) db.session.commit() on_course_modified.send( self, event_name=on_course_modified.name, user=current_user, course=course, data=model_changes) return marshal(course, dataformat.get_course())
def get(self, course_uuid): course = Course.get_active_by_uuid_or_404(course_uuid) require( READ, course, title="Instructors Unavailable", message= "Instructors can only be seen here by those enrolled in the course. Please double-check your enrollment in this course." ) restrict_user = not can(MANAGE, course) instructors = User.query \ .with_entities(User, UserCourse) \ .join(UserCourse, UserCourse.user_id == User.id) \ .filter( UserCourse.course_id == course.id, or_( UserCourse.course_role == CourseRole.instructor, UserCourse.course_role == CourseRole.teaching_assistant ) ) \ .order_by(User.lastname, User.firstname) \ .all() users = [] user_course = UserCourse(course_id=course.id) for u in instructors: if can(READ, user_course): users.append({ 'id': u.User.uuid, 'name': u.User.fullname_sortable, 'group_id': u.UserCourse.group_id, 'role': u.UserCourse.course_role.value }) else: users.append({ 'id': u.User.uuid, 'name': u.User.displayname, 'group_id': u.UserCourse.group_id, 'role': u.UserCourse.course_role.value }) on_classlist_instructor.send(self, event_name=on_classlist_instructor.name, user=current_user, course_id=course.id) return {'objects': users}
def get(self): if allow(MANAGE, Course()): courses = Course.query.filter_by(active=True).all() else: courses = [] for user_course in current_user.user_courses: if user_course.course.active and allow( MANAGE, Assignment(course_id=user_course.course_id)): courses.append(user_course.course) course_list = [{'id': c.uuid, 'name': c.name} for c in courses] on_teaching_course_get.send(self, event_name=on_teaching_course_get.name, user=current_user) return {'courses': course_list}