def extract_archive(self, filename): path = os.path.dirname(filename) if filename.endswith('.zip'): logger.debug("Unzip archive %s", filename) import zipfile with zipfile.ZipFile(filename) as zf: zf.extractall(path)
def require_success_message(self, response): document = html5lib.parse(response.content, transport_encoding=response.encoding) msg = document.find('.//h:span[@id="goodMsg1"]', NS) if msg is None: raise ParserError("No goodMsg1 in POST response", response, 'Post data:\n%s' % pprint.pformat(self._data), 'Files:\n%s' % pprint.pformat(self.files)) logger.debug("goodMsg1: %s", element_text_content(msg))
def is_course_id_valid(session, course_id=None): if course_id is None: course_id = session.course_id url = ( 'https://bb.au.dk/webapps/blackboard/execute/' + 'courseMain?course_id=%s' % course_id) response = session.get(url) document = html5lib.parse(response.content, encoding=response.encoding) content_panel_path = './/h:div[@id="contentPanel"]' content_panel = document.find(content_panel_path, NS) if content_panel is None: logger.debug("is_course_id_valid: No contentPanel") return True classes = (content_panel.get('class') or '').split() return 'error' not in classes
def is_course_id_valid(session, course_id=None): if course_id is None: course_id = session.course_id url = ('https://%s/webapps/blackboard/execute/' % DOMAIN + 'courseMain?course_id=%s' % course_id) response = session.get(url) document = html5lib.parse(response.content, transport_encoding=response.encoding) content_panel_path = './/h:div[@id="contentPanel"]' content_panel = document.find(content_panel_path, NS) if content_panel is None: logger.debug("is_course_id_valid: No contentPanel") return True classes = (content_panel.get('class') or '').split() return 'error' not in classes
def get_attempt_files(self, attempt): assert isinstance(attempt, Attempt) keys = 'submission comments files'.split() st = self.get_attempt_state(attempt) if all(k in st for k in keys) and 'score' not in st: logger.debug("Refresh attempt %s since it was fetched in an old " + "version of bbfetch", attempt.id) elif all(k in st for k in keys) and st['score'] != attempt.score: logger.debug("Refresh attempt %s since its score has changed", attempt.id) if (not all(k in st for k in keys) or 'score' not in st or st['score'] != attempt.score): self.refresh_attempt_files(attempt) st = self.get_attempt_state(attempt) used_filenames = set(['comments.txt']) files = [] def add_file(name, **data): if name in used_filenames: base, ext = os.path.splitext(name) name = base + attempt.id + ext data['filename'] = name used_filenames.add(name) files.append(data) if st['submission']: add_file('submission.txt', contents=st['submission']) if st['comments']: add_file('student_comments.txt', contents=st['comments']) if st.get('feedback'): used_filenames.remove('comments.txt') add_file('comments.txt', contents=st['feedback']) rubrics = self.get_rubrics(attempt) if rubrics: add_file('rubric.txt', contents='\n'.join(r.get_form_as_text() for r in rubrics)) for o in st.get('feedbackfiles', []): add_file(o['filename'], **o) for o in st['files']: add_file(o['filename'], **o) return files
def extract_zip(self, filename): path = os.path.dirname(filename) logger.debug("Unzip archive %s", filename) import zipfile with zipfile.ZipFile(filename) as zf: zf.extractall(path)
def extract_tar(self, filename): path = os.path.dirname(filename) logger.debug("Extract tar archive %s", filename) import tarfile with tarfile.open(filename) as f: f.extractall(path)
def extract_rar(self, filename): path = os.path.dirname(filename) logger.debug("Extract rar archive %s", filename) import rarfile with rarfile.RarFile(filename) as f: f.extractall(path)
def submit_grade(session, attempt_id, is_group_assignment, grade, text, filenames, rubrics): assert isinstance(session, BlackboardSession) if is_group_assignment: url = ('https://bb.au.dk/webapps/assignment/' + 'gradeAssignmentRedirector' + '?course_id=%s' % session.course_id + '&groupAttemptId=%s' % attempt_id) else: url = ('https://bb.au.dk/webapps/assignment/' + 'gradeAssignmentRedirector' + '?course_id=%s' % session.course_id + '&attempt_id=%s' % attempt_id) # We need to fetch the page to get the nonce response = session.get(url) document = html5lib.parse(response.content, encoding=response.encoding) form = document.find('.//h:form[@id="currentAttempt_form"]', NS) if form is None: raise ParserError("No <form id=currentAttempt_form>", response) fields = (form.findall('.//h:input', NS) + form.findall('.//h:textarea', NS)) data = [ (field.get('name'), form_field_value(field)) for field in fields if field.get('name') ] data_lookup = {k: i for i, (k, v) in enumerate(data)} def data_get(k, *args): if args: d, = args try: return data[data_lookup[k]][1] except KeyError: if args: return d raise def data_set(k, v): try: data[data_lookup[k]] = k, v except KeyError: data_lookup[k] = len(data) data.append((k, v)) def data_extend(kvs): for k, v in kvs: data_lookup[k] = len(data) data.append((k, v)) data_set('grade', str(grade)) data_set('feedbacktext', text) data_set('gradingNotestext', 'Submitted with https://github.com/Mortal/bbfetch') if rubrics: rubric_input = '%s_rubricEvaluation' % attempt_id rubric_data_str = data_get(rubric_input) rubric_data = json.loads(unquote(rubric_data_str)) for rubric_cells, rubric in zip(rubrics, rubric_data['rubrics']): rubric['client_changed'] = True for input_row, row in zip(rubric_cells, rubric['rows']): row['cell_id'] = input_row rubric_data_str = quote(json.dumps(rubric_data)) data_set(rubric_input, rubric_data_str) files = [] for i, filename in enumerate(filenames): base = os.path.basename(filename) data_extend([ ('feedbackFiles_attachmentType', 'L'), ('feedbackFiles_fileId', 'new'), ('feedbackFiles_artifactFileId', 'undefined'), ('feedbackFiles_artifactType', 'undefined'), ('feedbackFiles_artifactTypeResourceKey', 'undefined'), ('feedbackFiles_linkTitle', base), ]) with open(filename, 'rb') as fp: fdata = fp.read() files.append(('feedbackFiles_LocalFile%d' % i, (base, fdata))) if is_group_assignment: post_url = ( 'https://bb.au.dk/webapps/assignment//gradeGroupAssignment/submit') else: post_url = ( 'https://bb.au.dk/webapps/assignment//gradeAssignment/submit') if not files: # Blackboard requires the POST to be # Content-Type: multipart/form-data. # Unfortunately, requests can only make a form-data POST # if it has file-like input in the files list. files = [('dummy', io.StringIO(''))] try: response = session.post(post_url, data=data, files=files) except: logger.exception("data=%r files=%r", data, files) raise document = html5lib.parse(response.content, encoding=response.encoding) badmsg = document.find('.//h:span[@id="badMsg1"]', NS) if badmsg is not None: raise ParserError( "badMsg1: %s" % element_text_content(badmsg), response, 'Post data:\n%s' % pprint.pformat(data), 'Files:\n%s' % pprint.pformat(files)) msg = document.find('.//h:span[@id="goodMsg1"]', NS) if msg is None: raise ParserError( "No goodMsg1 in POST response", response, 'Post data:\n%s' % pprint.pformat(data), 'Files:\n%s' % pprint.pformat(files)) logger.debug("goodMsg1: %s", element_text_content(msg))