def test_time(self): self.setup_course() # UTC Time time = dt.datetime(month=1, day=20, year=2016, hour=12, minute=1) self.assertEquals(utils.local_time(time, self.course), 'Wed 01/20 04:01 AM') # DT Aware pacific = pytz.timezone('US/Pacific') localized = pacific.localize(time) self.assertEquals(utils.local_time(localized, self.course), 'Wed 01/20 12:01 PM') eastern = pytz.timezone('US/Eastern') localized = eastern.localize(time) self.assertEquals(utils.local_time(localized, self.course), 'Wed 01/20 09:01 AM')
def write_final_submission(zf, logger, assignment, student, seen, anonymize): """ Get the final submission STUDENT and write it into the zipfile ZF. """ student_user = student.user stats = assignment.user_status(student_user) backup = stats.final_subm if not backup: return if stats.group: group_emails = [User.email_by_id(m.user_id) for m in stats.group.members] else: group_emails = [student_user.email] group_str = '-'.join(sorted(group_emails)) if group_str in seen: return seen.add(group_str) course = assignment.course files = backup.files() if anonymize: for email in group_emails: if pii_present(files, email, assignment.course, logger): return identifier = '-'.join(sorted([student_hash(e) for e in group_emails])) folder = "{}/{}/".format(assignment.name.replace('/', '-'), identifier) else: folder = "{}/{}/{}".format(assignment.name.replace('/', '-'), group_str, backup.hashid) dump_info = { 'group': group_emails, 'scores': [s.export for s in stats.scores], 'submitter': User.email_by_id(backup.submitter_id), 'subm_time_local': local_time(stats.subm_time, course) } if backup.custom_submission_time: dump_info['custom_time_local'] = local_time(backup.custom_submission_time, course) zf.writestr("{}/info.json".format(folder), json.dumps(dump_info)) for name, contents in files.items(): # Write the file to the in-memory zip zf.writestr("{}/{}".format(folder, name), contents)
def export_grades(): logger = jobs.get_job_logger() current_user = jobs.get_current_job().user course = Course.query.get(jobs.get_current_job().course_id) assignments = course.assignments students = (Enrollment.query.options(db.joinedload('user')).filter( Enrollment.role == STUDENT_ROLE, Enrollment.course == course).all()) headers, assignments = get_headers(assignments) logger.info("Using these headers:") for header in headers: logger.info('\t' + header) logger.info('') total_students = len(students) users = [student.user for student in students] user_ids = [user.id for user in users] all_scores = collect_all_scores(assignments, user_ids) with io.StringIO() as f: writer = csv.writer(f) writer.writerow(headers) # write headers for i, student in enumerate(students, start=1): row = export_student_grades(student, assignments, all_scores) writer.writerow(row) if i % 50 == 0: logger.info('Exported {}/{}'.format(i, total_students)) f.seek(0) created_time = local_time(dt.datetime.now(), course, fmt='%b-%-d %Y at %I-%M%p') csv_filename = '{course_name} Grades ({date}).csv'.format( course_name=course.display_name, date=created_time) # convert to bytes for csv upload csv_bytes = io.BytesIO(bytearray(f.read(), 'utf-8')) upload = ExternalFile.upload(csv_bytes, user_id=current_user.id, name=csv_filename, course_id=course.id, prefix='jobs/exports/{}/'.format( course.offering)) logger.info('\nDone!\n') logger.info("Saved as: {0}".format(upload.object_name)) return "/files/{0}".format(encode_id(upload.id))
def export_assignment(assignment_id, anonymized): """ Generate a zip file of submissions from enrolled students. Final Submission: One submission per student/group Zip Strucutre: cal-cs61a../[email protected]@b.com/abc12d/hog.py Anonymized: Submission without identifying info Zip Strucutre: cal-cs61a../{hash}/hog.py """ logger = jobs.get_job_logger() assignment = Assignment.query.get(assignment_id) requesting_user = jobs.get_current_job().user if not assignment: logger.warning("No assignment found") raise Exception("No Assignment") if not Assignment.can(assignment, requesting_user, "download"): raise Exception("{} does not have enough permission" .format(requesting_user.email)) if anonymized: logger.info("Starting anonymized submission export") else: logger.info("Starting final submission export") course = assignment.course with io.BytesIO() as bio: # Get a handle to the in-memory zip in append mode with zipfile.ZipFile(bio, "w", zipfile.ZIP_DEFLATED, False) as zf: zf.external_attr = 0o655 << 16 export_loop(bio, zf, logger, assignment, anonymized) created_time = local_time(dt.datetime.now(), course, fmt='%m-%d-%I-%M-%p') zip_name = '{}_{}.zip'.format(assignment.name.replace('/', '-'), created_time) bio.seek(0) # Close zf handle to finish writing zipfile logger.info("Uploading...") upload = ExternalFile.upload(bio, user_id=requesting_user.id, name=zip_name, course_id=course.id, prefix='jobs/exports/{}/'.format(course.offering)) logger.info("Saved as: {0}".format(upload.object_name)) msg = "/files/{0}".format(encode_id(upload.id)) return msg
def generate_csv(): """ Generate csv export of scores for assignment. Num Queries: ~2N queries for N scores. """ # Yield Column Info as first row yield ','.join(items) + '\n' for score in query: csv_file = StringIO() csv_writer = csv.DictWriter(csv_file, fieldnames=items) submitters = score.backup.enrollment_info() group = [s.user.email for s in submitters] time_str = utils.local_time(score.backup.created, current_course) for submitter in submitters: data = {'email': submitter.user.email, 'time': time_str, 'is_late': score.backup.is_late, 'group': group} data.update(submitter.export) data.update(score.export) csv_writer.writerow(data) yield csv_file.getvalue()
def generate_csv(): """ Generate csv export of scores for assignment. Num Queries: ~2N queries for N scores. """ # Yield Column Info as first row yield ','.join(items) + '\n' for score in query: csv_file = StringIO() csv_writer = csv.DictWriter(csv_file, fieldnames=items) submitters = score.backup.enrollment_info() group = [s.user.email for s in submitters] time_str = utils.local_time(score.backup.created, course) for submitter in submitters: data = {'email': submitter.user.email, 'time': time_str, 'is_late': score.backup.is_late, 'group': group} data.update(submitter.export) data.update(score.export) csv_writer.writerow(data) yield csv_file.getvalue()
def export_grades(): logger = jobs.get_job_logger() current_user = jobs.get_current_job().user course = Course.query.get(jobs.get_current_job().course_id) assignments = course.assignments students = (Enrollment.query .options(db.joinedload('user')) .filter(Enrollment.role == STUDENT_ROLE, Enrollment.course == course) .all()) headers, assignments = get_headers(assignments) logger.info("Using these headers:") for header in headers: logger.info('\t' + header) logger.info('') total_students = len(students) with io.StringIO() as f: writer = csv.writer(f) writer.writerow(headers) # write headers for i, student in enumerate(students, start=1): row = export_student_grades(student, assignments) writer.writerow(row) if i % 50 == 0: logger.info('Exported {}/{}'.format(i, total_students)) f.seek(0) created_time = local_time(dt.datetime.now(), course, fmt='%b-%-d %Y at %I-%M%p') csv_filename = '{course_name} Grades ({date}).csv'.format( course_name=course.display_name, date=created_time) # convert to bytes for csv upload csv_bytes = io.BytesIO(bytearray(f.read(), 'utf-8')) upload = ExternalFile.upload(csv_bytes, user_id=current_user.id, name=csv_filename, course_id=course.id, prefix='jobs/exports/{}/'.format(course.offering)) logger.info('\nDone!\n') logger.info("Saved as: {0}".format(upload.object_name)) return "/files/{0}".format(encode_id(upload.id))