def getCourses(req): """ Returns a JSON object containing the list of available courses """ req.content_type = 'text/html' s = Session.Session(req) if s.is_new(): s.invalidate() return json.dumps({ 'errorType': websutil.ERR_AUTH, 'errorMessage': "", 'errorTrace': "" }) # Reset the timeout s.save() course_arr = [] strout = websutil.OutputString() try: clist = CourseList() for course_id in clist.course_names(): course_cfg_fname = clist.course_config(course_id) course_cfg = StorerCourseConfig(course_cfg_fname) course_title = course_cfg.course_name() course_arr.append({'id': course_id, 'title': course_title}) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': websutil.ERR_EXCEPTION, 'errorMessage': "", 'errorTrace': strout.get() }) return json.dumps(course_arr)
def get_user_from_auth_files(username, password): """Search all courseses for auth_files and if we can login in any course, return the login from there""" for coursecfg_fname in CourseList().course_configs(): vmpaths = paths.VmcheckerPaths( StorerCourseConfig(coursecfg_fname).root_path()) r = get_user_from_auth_file(vmpaths, username, password) if not r is None: return r return None
def getAssignmentAccountName(courseId, assignmentId, username, strout): try: vmcfg = StorerCourseConfig(CourseList().course_config(courseId)) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "", 'errorTrace': strout.get() }) vmpaths = paths.VmcheckerPaths(vmcfg.root_path()) with opening_course_db(vmpaths.db_file()) as course_db: return course_db.get_assignment_account(assignmentId, username)
def getUserUploadedMd5Helper(courseId, assignmentId, username, strout): """Get the current MD5 sum submitted for a given username on a given assignment""" try: vmcfg = StorerCourseConfig(CourseList().course_config(courseId)) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "", 'errorTrace': strout.get() }) (_, account) = getAssignmentAccountName(courseId, assignmentId, username, strout) vmpaths = paths.VmcheckerPaths(vmcfg.root_path()) submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account) md5_fpath = paths.submission_md5_file(submission_dir) md5_result = {} try: if os.path.exists(paths.submission_config_file( submission_dir)) and os.path.isfile(md5_fpath): sss = submissions.Submissions(vmpaths) upload_time_str = sss.get_upload_time_str(assignmentId, account) md5_result['fileExists'] = True with open(md5_fpath, 'r') as f: md5_result['md5Sum'] = f.read(32) md5_result['uploadTime'] = upload_time_str else: md5_result['fileExists'] = False return json.dumps(md5_result) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "", 'errorTrace': strout.get() })
def getAssignmentsHelper(courseId, currentUser, strout): try: vmcfg = StorerCourseConfig(CourseList().course_config(courseId)) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "Unable to load course config", 'errorTrace': strout.get() }) assignments = vmcfg.assignments() sorted_assg = sorted( assignments, lambda x, y: int(assignments.get(x, "OrderNumber")) - int( assignments.get(y, "OrderNumber"))) assg_arr = [] vmpaths = paths.VmcheckerPaths(vmcfg.root_path()) with opening_course_db(vmpaths.db_file()) as course_db: for key in sorted_assg: if assignments.is_hidden( key) and not currentUser in vmcfg.admin_list(): continue a = {} a['assignmentId'] = key a['assignmentTitle'] = assignments.get(key, "AssignmentTitle") a['assignmentStorage'] = assignments.getd(key, "AssignmentStorage", "") if a['assignmentStorage'].lower() == "large": a['assignmentStorageHost'] = assignments.get( key, "AssignmentStorageHost") a['assignmentStorageBasepath'] = assignments.storage_basepath( \ assignments.get(key, "AssignmentStorageBasepath"), currentUser) a['deadline'] = assignments.get(key, "Deadline") a['statementLink'] = assignments.get(key, "StatementLink") team = course_db.get_user_team_for_assignment(key, currentUser) if team is not None: a['team'] = team assg_arr.append(a) return json.dumps(assg_arr)
def get_storagedir_contents(courseId, assignmentId, account): """Get the content of a the archive coresponding to a MD5Submission-type homework""" client = paramiko.SSHClient() try: vmcfg = StorerCourseConfig(CourseList().course_config(courseId)) assignments = vmcfg.assignments() storage_hostname = assignments.get(assignmentId, 'AssignmentStorageHost') storage_username = assignments.get(assignmentId, 'AssignmentStorageQueryUser') storage_basepath = assignments.storage_basepath( \ assignments.get(assignmentId, 'AssignmentStorageBasepath') , account) client.load_system_host_keys(vmcfg.known_hosts_file()) client.connect(storage_hostname, username=storage_username, key_filename=vmcfg.storer_sshid(), look_for_keys=False) cmd = "find " + storage_basepath + '/' + account + \ " \( ! -regex '.*/\..*' \) -type f" stdin, stdout, stderr = client.exec_command(cmd) result = [] for d in stdout.readlines(): result.append({'fileName': d}) for f in [stdin, stdout, stderr]: f.close() return json.dumps(result) except: strout = OutputString() traceback.print_exc(file=strout) return json.dumps({'errorTrace': strout.get()}, indent=4) finally: client.close()
def getAllGradesHelper(courseId, username, strout): try: # XXX: DON'T DO THIS: performance degrades very much! #update_db.update_grades(courseId) vmcfg = StorerCourseConfig(CourseList().course_config(courseId)) vmpaths = paths.VmcheckerPaths(vmcfg.root_path()) assignments = vmcfg.assignments() sorted_assg = sorted( assignments, lambda x, y: int(assignments.get(x, "OrderNumber")) - int( assignments.get(y, "OrderNumber"))) # Check if the current user is allowed to view all the grades # TODO: This should be implemented neater using some group # and permission model. user_can_view_all = False if vmcfg.public_results() or username in vmcfg.admin_list(): user_can_view_all = True user_grade_rows = None team_grade_rows = None with opening_course_db(vmpaths.db_file()) as course_db: if user_can_view_all: user_grade_rows = course_db.get_user_grades() team_grade_rows = course_db.get_team_grades() else: # Get all the individual grades that the user is allowed to see user_grade_rows = course_db.get_user_and_teammates_grades( username) # Get all the team grades that the user is allowed to see team_grade_rows = course_db.get_user_team_grades(user=username) ret = [] grades = {} for row in user_grade_rows: user, assignment, grade = row if not assignment in vmcfg.assignments(): continue if not vmcfg.assignments().show_grades_before_deadline(assignment): deadline = time.strptime( vmcfg.assignments().get(assignment, 'Deadline'), DATE_FORMAT) deadtime = time.mktime(deadline) if time.time() < deadtime: continue if vmcfg.assignments().is_hidden( assignment) and username not in vmcfg.admin_list(): continue grades.setdefault(user, {})[assignment] = grade for user in sorted(grades.keys()): ret.append({ 'gradeOwner': 'user', 'name': user, 'results': grades.get(user) }) grades = {} for row in team_grade_rows: team, assignment, grade = row if not assignment in vmcfg.assignments(): continue if not vmcfg.assignments().show_grades_before_deadline(assignment): deadline = time.strptime( vmcfg.assignments().get(assignment, 'Deadline'), DATE_FORMAT) deadtime = time.mktime(deadline) if time.time() < deadtime: continue grades.setdefault(team, {})[assignment] = grade for team in sorted(grades.keys()): ret.append({ 'gradeOwner': 'team', 'name': team, 'results': grades.get(team) }) return json.dumps(ret) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "", 'errorTrace': strout.get() })
def getResultsHelper(courseId, assignmentId, currentUser, strout, username=None, teamname=None, currentTeam=None): # assume that the session was already checked if username != None and teamname != None: return json.dumps({ 'errorType': ERR_OTHER, 'errorMessage': "Can't query both user and team results at the same time.", 'errorTrace': "" }) try: vmcfg = StorerCourseConfig(CourseList().course_config(courseId)) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "", 'errorTrace': strout.get() }) # Check if the current user is allowed to view any other user's grade. # TODO: This should be implemented neater using some group # and permission model. is_authorized = vmcfg.public_results() or \ currentUser in vmcfg.admin_list() or \ username == currentUser or \ teamname == currentTeam if not is_authorized: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "User is not authorized to view results.", 'errorTrace': strout.get() }) vmpaths = paths.VmcheckerPaths(vmcfg.root_path()) account = None if username != None: # Get the individual results for this user account = username isTeamAccount = False elif teamname != None: # Get the team results for this team account = teamname isTeamAccount = True else: # Check if the user is part of a team with a mutual account for this submission (isTeamAccount, account) = getAssignmentAccountName(courseId, assignmentId, currentUser, strout) submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account) r_path = paths.dir_submission_results(submission_dir) assignments = vmcfg.assignments() ignored_vmrs = assignments.ignored_vmrs(assignmentId) try: isGraded = False result_files = [] if os.path.isdir(r_path): update_db.update_grades(courseId, account=account, assignment=assignmentId) for fname in os.listdir(r_path): # skill all files not ending in '.vmr' if not fname.endswith('.vmr'): continue if fname in ignored_vmrs: continue f_path = os.path.join(r_path, fname) if os.path.isfile(f_path): overflow_msg = '' f_size = os.path.getsize(f_path) if f_size > MAX_VMR_FILE_SIZE: overflow_msg = '\n\n' + _( 'File truncated! Actual size') + ': ' + str( f_size) + ' ' + _('bytes') + '\n' # decode as utf-8 and ignore any errors, because # characters will be badly encoded as json. with codecs.open(f_path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read(MAX_VMR_FILE_SIZE) + overflow_msg content = xssescape(content) result_files.append({fname: content}) if fname == 'grade.vmr' and \ "".join(content.split()) not in submissions.GENERATED_STATUSES: isGraded = True if (len(result_files) == 1 and result_files[0].keys()[0] == "grade.vmr") and \ not vmcfg.assignments().submit_only(assignmentId): msg = _("In the meantime have a fortune cookie") + ": <blockquote>" try: process = subprocess.Popen('/usr/games/fortune', shell=False, stdout=subprocess.PIPE) msg += process.communicate()[0] + "</blockquote>" except: msg += "Knock knock. Who's there? [Silence] </blockquote>" result_files = [{'fortune.vmr': msg}] result_files.append({ 'queue-contents.vmr': get_test_queue_contents(vmcfg, courseId) }) if 'submission.vmr' not in ignored_vmrs: result_files.append({ 'submission.vmr': submission_upload_info(vmcfg, courseId, assignmentId, account, isTeamAccount, isGraded) }) result_files = sortResultFiles(result_files) return json.dumps(result_files) except: traceback.print_exc(file=strout) return json.dumps({ 'errorType': ERR_EXCEPTION, 'errorMessage': "", 'errorTrace': strout.get() })
def validate_md5_submission(courseId, assignmentId, account, archiveFileName): """Checks whether a MD5Submission is valid: * checks that the uploaded md5 corresponds to the one of the machine * checks that the archive uploaded by the student is a zip file On success returns (True,). On failure reports the source of the failure: - (False, 'md5') - the uploaded md5 does not match the one computed on the archive - (False, 'zip') - the uploaded archive is not zip. """ md5_calculated = "" md5_uploaded = "" archive_file_type = "" client = paramiko.SSHClient() try: vmcfg = StorerCourseConfig(CourseList().course_config(courseId)) assignments = vmcfg.assignments() storage_hostname = assignments.get(assignmentId, 'AssignmentStorageHost') storage_username = assignments.get(assignmentId, 'AssignmentStorageQueryUser') storage_basepath = assignments.storage_basepath( \ assignments.get(assignmentId, 'AssignmentStorageBasepath'), account) client.load_system_host_keys(vmcfg.known_hosts_file()) client.connect(storage_hostname, username=storage_username, key_filename=vmcfg.storer_sshid(), look_for_keys=False) archive_abs = os.path.join(storage_basepath, account, archiveFileName) # XXX: This will take ages to compute! I wonder how many # connections will Apache hold. stdin, stdout, stderr = client.exec_command("md5sum " + QuoteForPOSIX(archive_abs)) md5_calculated = stdout.readline().split()[0] for f in [stdin, stdout, stderr]: f.close() stdin, stdout, stderr = client.exec_command("file " + QuoteForPOSIX(archive_abs)) archive_file_type = stdout.readline()[len(archive_abs):].split( )[1].lower() for f in [stdin, stdout, stderr]: f.close() vmpaths = paths.VmcheckerPaths(vmcfg.root_path()) submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account) md5_fpath = paths.submission_md5_file(submission_dir) if os.path.isfile(md5_fpath): with open(md5_fpath, 'r') as f: md5_uploaded = f.read(32) except: strout = OutputString() traceback.print_exc(file=strout) return json.dumps({'errorTrace': strout.get()}, indent=4) finally: client.close() if not md5_calculated == md5_uploaded: return (False, MD5_ERR_BAD_MD5) # report the type of the problem if not archive_file_type == "zip": return (False, MD5_ERR_BAD_ZIP) # report the type of the problem return (True, ) # no problemo