def fork_group(lti=lti): ''' Adds a group to a course''' # Get arguments assignment_group_id = int(request.values.get('assignment_group_id')) assignment_group = AssignmentGroup.by_id(assignment_group_id) is_embedded = ('embed' == request.values.get('menu', "select")) # Verify exists check_resource_exists(assignment_group, "Assignment Group", assignment_group_id) # Verify permissions require_course_instructor(g.user, assignment_group.course_id) # Perform action new_assignment_group = AssignmentGroup.new( owner_id=g.user.id, course_id=assignment_group.course_id, name=assignment_group.name) new_assignment_group.forked_id = assignment_group_id new_assignment_group.forked_version = assignment_group.version # Result select_url = get_select_menu_link(new_assignment_group.id, new_assignment_group.name, is_embedded, True) return jsonify(success=True, id=new_assignment_group.id, name=new_assignment_group.name, select=select_url)
def remove_group(lti=lti): ''' Removes a group from a course''' assignment_group_id = int(request.values.get('assignment_group_id')) assignment_group = AssignmentGroup.by_id(assignment_group_id) # Verify exists check_resource_exists(assignment_group, "Assignment Group", assignment_group_id) # Verify permissions require_course_instructor(g.user, assignment_group.course_id) # Perform action AssignmentGroup.remove(assignment_group.id) # Result return jsonify(success=True)
def import_bundle(bundle, owner_id, course_id=None, update=True): if 'course' in bundle: course = Course.decode_json(bundle['course'], owner_id=owner_id) db.session.add(course) db.session.commit() else: course = Course.by_id(course_id) assignment_remap = {} assignments = bundle.get('assignments', []) for assignment_data in natsorted(assignments, key=lambda a: a['name']): assignment = Assignment.decode_json(assignment_data, course_id=course.id, owner_id=owner_id) assignment_remap[assignment_data['url']] = assignment.id group_remap = {} groups = bundle.get('groups', []) for group_data in natsorted(groups, key=lambda g: g['name']): group = AssignmentGroup.decode_json(group_data, course_id=course.id, owner_id=owner_id) group_remap[group_data['url']] = group.id memberships = bundle.get('memberships', []) for member_data in sorted(memberships, key=sorter): assignment_id = assignment_remap[member_data['assignment_url']] group_id = group_remap[member_data['assignment_group_url']] member = AssignmentGroupMembership.decode_json( member_data, assignment_id=assignment_id, assignment_group_id=group_id) return True
def generate_maintable(zip_file, course_id, assignment_group_ids): code_states, latest_code_states, scores = {}, {}, {} query = Log.query.filter_by(course_id=course_id) if assignment_group_ids is not None: assignment_ids = [ assignment.id for group_id in assignment_group_ids for assignment in AssignmentGroup.by_id(group_id).get_assignments() ] query = query.filter(Log.assignment_id.in_(assignment_ids)) estimated_size = query.count() logs = query.order_by(Log.date_created.asc()).yield_per(100) tempdir = tempfile.mkdtemp() temppath = os.path.join(tempdir, 'MainTable.csv') #with io.StringIO() as maintable_file: with open(temppath, 'w') as maintable_file: writer = csv.writer(maintable_file, **PROGSNAP_CSV_WRITER_OPTIONS) writer.writerow(HEADERS) order_id = 0 for log in tqdm(logs, total=estimated_size): writer.writerow( to_progsnap_event(log, order_id, code_states, latest_code_states, scores)) order_id += 1 #zip_file.writestr("MainTable.csv", maintable_file.getvalue()) zip_file.write(temppath, "MainTable.csv") shutil.rmtree(tempdir) return "MainTable.csv", code_states
def get_groups_submissions(group_id, user_id, course_id): group = AssignmentGroup.by_id(group_id) check_resource_exists(group, "AssignmentGroup", group_id) assignments = group.get_assignments() submissions = [ assignment.load(user_id, course_id=course_id) for assignment in assignments ] return group, assignments, submissions
def get_existing(cls, data): group_url = data['assignment_group_url'] assignment_url = data['assignment_url'] assignment = Assignment.by_url(assignment_url) group = AssignmentGroup.by_url(group_url) if not assignment or not group: return None return (AssignmentGroupMembership.query.filter_by( assignment_group_id=group.id, assignment_id=assignment.id).first())
def run(self, **kwargs): from models.user import User from models.course import Course from models.role import Role from models.assignment_group import AssignmentGroup from models.assignment_group_membership import AssignmentGroupMembership from models.assignment import Assignment default_course = Course.query.first() print("Adding Teacher") teacher = User(first_name="Klaus", last_name="Bart", password=hash_password("password"), confirmed_at=datetime.datetime.now(), active=True, email="*****@*****.**") db.session.add(teacher) db.session.flush() db.session.add( Role(name='instructor', course_id=default_course.id, user_id=teacher.id)) print("Adding Student") student = User(first_name="Ada", last_name="Bart", password=hash_password("password"), confirmed_at=datetime.datetime.now(), active=True, email="*****@*****.**") db.session.add(student) db.session.flush() db.session.add( Role(name='student', course_id=default_course.id, user_id=student.id)) print("Adding basic assignments") basic_group = AssignmentGroup(name="First Group", course_id=default_course.id, owner_id=teacher.id) db.session.add(basic_group) db.session.flush() for i in range(5): assignment = Assignment(name="Problem {}".format(i), instructions="Complete this problem", owner_id=teacher.id, course_id=default_course.id) db.session.add(assignment) db.session.flush() db.session.add( AssignmentGroupMembership(assignment_group_id=basic_group.id, assignment_id=assignment.id)) db.session.commit() print("Complete")
def move_membership(lti=None): # Get arguments assignment_id = int(request.values.get('assignment_id')) old_group_id = int(request.values.get('old_group_id')) new_group_id = int(request.values.get('new_group_id')) assignment = Assignment.by_id(assignment_id) # Verify exists check_resource_exists(assignment, "Assignment", assignment_id) # Verify permissions require_course_instructor(g.user, assignment.course_id) # Verify permissions if new_group_id != -1: new_assignment_group = AssignmentGroup.by_id(new_group_id) require_course_instructor(g.user, new_assignment_group.course_id) if old_group_id != -1: old_assignment_group = AssignmentGroup.by_id(old_group_id) require_course_instructor(g.user, old_assignment_group.course_id) # Perform action AssignmentGroupMembership.move_assignment(assignment_id, new_group_id) # Result return jsonify(success=True)
def edit_settings(lti=lti): # Get arguments assignment_group_id = int(request.values.get('assignment_group_id')) assignment_group = AssignmentGroup.by_id(assignment_group_id) ip_ranges = request.values.get('ip_ranges') passcode = request.values.get('passcode') # Verify exists check_resource_exists(assignment_group, "Assignment Group", assignment_group_id) # Verify permissions require_course_instructor(g.user, assignment_group.course_id) # Perform action if request.method == 'POST': if ip_ranges is not None: for assignment in assignment_group.get_assignments(): assignment.edit(dict(ip_ranges=ip_ranges)) assignment.update_setting("passcode", passcode) return redirect(request.url) # Result else: assignments = assignment_group.get_assignments() passcode = assignments[0].get_setting("passcode", "") existing_ip_ranges = [ assignment.ip_ranges for assignment in assignments ] merged_duplicates = set(existing_ip_ranges) warning = "" if len(merged_duplicates) == 1: ip_ranges = merged_duplicates.pop() elif merged_duplicates: ip_ranges = existing_ip_ranges[0] warning = "This assignment has multiple IP ranges: <pre>{}</pre>".format( "\n".join(existing_ip_ranges)) return ''' <!doctype html> <title>Edit Assignment Group Settings</title> <h1>Edit Assignment Group Settings</h1> <p>Assignment: {group_name}</p> <p>{warning}</p> <form action="" method=post> <p>IP Ranges: <input type=text name=ip_ranges value="{ip_ranges}"><br> Passcode: <input type=text name=passcode value="{passcode}"><br> <input type=submit value=Change> </form> '''.format(group_name=assignment_group.name, ip_ranges=ip_ranges if ip_ranges else "", passcode=passcode if passcode else "", warning=warning)
def encode_json(self): group = AssignmentGroup.by_id(self.assignment_group_id) group_url = group.url if group else None assignment = Assignment.by_id(self.assignment_id) assignment_url = assignment.url if assignment else None return { '_schema_version': 1, 'assignment_group_id': self.assignment_group_id, 'assignment_group_url': group_url, 'assignment_id': self.assignment_id, 'assignment_url': assignment_url, 'position': self.position, 'id': self.id, 'date_modified': datetime_to_string(self.date_modified), 'date_created': datetime_to_string(self.date_created) }
def edit_group(lti=lti): # Get arguments assignment_group_id = int(request.values.get('assignment_group_id')) assignment_group = AssignmentGroup.by_id(assignment_group_id) new_name = request.values.get('new_name') new_url = request.values.get('new_url') # Verify exists check_resource_exists(assignment_group, "Assignment Group", assignment_group_id) # Verify permissions require_course_instructor(g.user, assignment_group.course_id) # Perform action changed = assignment_group.edit(dict(name=new_name, url=new_url)) # Result return jsonify(success=True, name=assignment_group.name, url=assignment_group.url)
def export(): user = load_api_user() assignment_url = request.json.get("assignment_url") assignment_id = request.json.get("assignment_id") if assignment_id or assignment_url: if assignment_url: assignment = Assignment.by_url(assignment_url) else: assignment = Assignment.by_id(assignment_id) check_resource_exists(assignment, "Assignment", assignment_id or assignment_url) if not user.is_instructor(assignment.course_id): return abort(400, "Not an instructor in this assignments' course.") return json.dumps(export_bundle(assignments=[assignment])) group_id = request.json.get("assignment_group_id") if group_id: assignment_group = AssignmentGroup.by_id(group_id) assignments = assignment_group.get_assignments() memberships = assignment_group.get_memberships() if not user.is_instructor(assignment_group.course_id): return abort( 400, "Not an instructor in this assignment group's course.") for assignment in assignments: if not user.is_instructor(assignment.course_id): return abort(400, "Not an instructor in this assignments' course.") bundle = export_bundle(groups=[assignment_group], assignments=assignments, memberships=memberships) return json.dumps(bundle) course_id = request.json.get("course_id") if course_id: if not user.is_instructor(course_id): return abort(400, "Not an instructor in this course.") course = Course.by_id(course_id) groups = course.get_assignment_groups() assignment_and_memberships = course.get_assignments() assignments = [a for a, m in assignment_and_memberships] memberships = [m for a, m in assignment_and_memberships] bundle = export_bundle(groups=groups, assignments=assignments, memberships=memberships) return json.dumps(bundle) abort(400, "No assignment_id or assignment_group_id given")
def export(): assignment_group_id = int(request.values.get('assignment_group_id')) assignment_group = AssignmentGroup.by_id(assignment_group_id) course_id = get_course_id(True) user, user_id = get_user() # Verify exists check_resource_exists(assignment_group, "Assignment Group", assignment_group_id) # Perform action assignments = assignment_group.get_assignments() memberships = assignment_group.get_memberships() bundle = export_bundle(groups=[assignment_group], assignments=assignments, memberships=memberships) filename = assignment_group.get_filename() return Response(json.dumps(bundle, indent=2), mimetype='application/json', headers={ 'Content-Disposition': 'attachment;filename={}'.format(filename) })
def add_group(lti=lti): ''' Adds a group to a course''' # Get arguments course_id = int(request.values.get('course_id')) new_name = request.values.get('name') new_url = request.values.get('url') is_embedded = ('embed' == request.values.get('menu', "select")) # Verify permissions require_course_instructor(g.user, course_id) # Perform action assignment_group = AssignmentGroup.new(owner_id=g.user.id, course_id=course_id, name=new_name, url=new_url) # Result select_url = get_select_menu_link(assignment_group.id, assignment_group.name, is_embedded, True) return jsonify(success=True, id=assignment_group.id, url=assignment_group.url, name=assignment_group.name, select=select_url)
def run(self, owner_id, **kwargs): from models.user import User from models.course import Course from models.role import Role from models.assignment import Assignment from models.assignment_group import AssignmentGroup from models.assignment_group_membership import AssignmentGroupMembership owner_id = int(owner_id) maze_course = Course.new('Maze Course', owner_id, 'public') maze_group = AssignmentGroup.new(owner_id, maze_course.id, "Maze Game") for level in range(10): maze_level = Assignment.new(owner_id, maze_course.id, 'maze', level=str(1 + level)) db.session.add(maze_level) db.session.flush() membership = AssignmentGroupMembership.move_assignment( maze_level.id, maze_group.id) db.session.add(membership) db.session.commit()
def generate_link_assignments(zip_file, course_id, assignment_group_ids): if assignment_group_ids is None: assignments = Log.get_assignments_for_course(course_id) all_groups = set() assignment_groups = {} for assignment in assignments: groups = AssignmentGroup.by_assignment(assignment.id) all_groups.update(groups) assignment_groups[assignment.id] = groups else: all_groups = [ AssignmentGroup.by_id(group_id) for group_id in assignment_group_ids ] assignment_groups = {} assignments = set() for group in all_groups: for assignment in group.get_assignments(): if assignment.id not in assignment_groups: assignment_groups[assignment.id] = set() assignment_groups[assignment.id].add(group) assignments.add(assignment) with io.StringIO() as assignment_file: assignment_writer = csv.writer(assignment_file, **PROGSNAP_CSV_WRITER_OPTIONS) assignment_writer.writerow([ "AssignmentId", "X-Version", "X-Name", "X-URL", "X-Instructions", "X-Reviewed", "X-Hidden", "X-Settings", "X-Code.OnRun", "X-Code.OnChange", "X-Code.OnEval", "X-Code.Starting", "X-Code.ExtraInstructor", "X-Code.ExtraStarting", "X-Forked.Id", "X-Forked.Version", "X-Owner.Id", "X-Course.Id", "X-AssignmentGroup.Ids" ]) for assignment in natsorted(assignments, key=lambda a: a.name): assignment_writer.writerow([ assignment.id, assignment.version, assignment.name, assignment.url, assignment.instructions, assignment.reviewed, assignment.hidden, assignment.settings, assignment.on_run, assignment.on_change, assignment.on_eval, assignment.starting_code, assignment.extra_instructor_files, assignment.extra_starting_files, assignment.forked_id, assignment.forked_version, assignment.owner_id, assignment.course_id, ", ".join( map(str, (g.id for g in assignment_groups[assignment.id]))) ]) zip_file.writestr("LinkTables/Assignment.csv", assignment_file.getvalue()) yield "LinkTables/Assignment.csv" with io.StringIO() as group_file: group_writer = csv.writer(group_file, **PROGSNAP_CSV_WRITER_OPTIONS) group_writer.writerow([ "AssignmentGroupId", "X-Version", "X-Name", "X-URL", "X-Forked.Id", "X-Forked.Version", "X-Owner.Id", "X-Course.Id" ]) for group in natsorted(all_groups, key=lambda g: g.name): group_writer.writerow([ group.id, group.version, group.name, group.url, group.forked_id, group.forked_version, group.owner_id, group.course_id, ]) zip_file.writestr("LinkTables/AssignmentGroup.csv", group_file.getvalue()) yield "LinkTables/AssignmentGroup.csv"
def parse_assignment_load(): # Lookup Code assignment_group_id = parse_lookup_code() # Assignment Group ID if assignment_group_id is None: assignment_group_id = maybe_int( request.args.get('assignment_group_id')) # Exact "url" code for group if assignment_group_id is None: assignment_group_id = AssignmentGroup.id_by_url( request.args.get('assignment_group_url')) # Assignment ID current_assignment_id = maybe_int(request.args.get('assignment_id')) # Exact "url" code for assignment if current_assignment_id is None: current_assignment_id = Assignment.id_by_url( request.args.get('assignment_url')) # User user = g.get('user', None) user_id = user.id if user else None # Course ID of the user course_id = maybe_int(request.args.get('course_id', None)) if course_id is None: course_id = int(g.course.id) if 'course' in g and g.course else None # LTI submission URL new_submission_url = request.form.get('lis_result_sourcedid', None) # Embedded? embed = request.values.get('embed', 'false').lower() == 'true' # Get group assignment_group = AssignmentGroup.by_id(assignment_group_id) # Get assignments if assignment_group is None: assignment = Assignment.by_id(current_assignment_id) if assignment: assignments = [assignment] else: assignments = [] else: assignments = assignment_group.get_assignments() # Potentially adjust assignment_id if current_assignment_id is None and assignments: current_assignment_id = assignments[0].id # Get submissions if user_id is None or course_id is None: submissions = [] else: submissions = [ assignment.load_or_new_submission(user_id, course_id, new_submission_url, assignment_group_id) for assignment in assignments ] # Determine the users' role in relation to this information role = user.determine_role(assignments, submissions) if user else "anonymous" if role in ("student", "anonymous"): # Check for any IP locked assignments for assignment in assignments: if not assignment.is_allowed(request.remote_addr): return abort( 403, "You cannot access this assignment from your current location: " + request.remote_addr) # Check passcode passcode_protected = False for assignment in assignments: if assignment.has_passcode() and not passcode_protected: passcode_protected = True # Combine the submissions and assignments group = list(zip(assignments, submissions)) # Okay we've got everything return dict(group=group, assignment_group=assignment_group, assignments=assignments, submissions=submissions, assignment_group_id=assignment_group_id, current_assignment_id=current_assignment_id, user=user, user_id=user_id, role=role, course_id=course_id, embed=embed, passcode_protected=passcode_protected)