def submit(self, post_url=None): if post_url is None: post_url = self.post_url print("POST to", post_url) if self.enctype_formdata and not self.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. self.files = [('dummy', io.StringIO(''))] try: data = [d for d in self._data if d is not None] response = self._session.post(post_url, data=data, files=self.files) except: logger.exception("data=%r files=%r", data, self.files) raise self._log_badmsg(response) response.history = self._history + list(response.history) return response
def execute_from_command_line(cls): parser = cls.get_argument_parser() args = parser.parse_args() blackboard.configure_logging(quiet=args.quiet) not_implemented = [] try: try: course = cls.get_course(args) except NotImplementedError: not_implemented.append('Grading.get_course') try: username = cls.get_username(args) except NotImplementedError: not_implemented.append('Grading.get_username') except Exception as exn: parser.error(str(exn)) if not_implemented: parser.error("You must implement %s" % ' and '.join(not_implemented)) session = cls.session_class('cookies.txt', username, course) grading = cls(session) grading.override_get_password(args) try: grading.load('grading.json') grading.main(args, session, grading) except ParserError as exn: logger.error("Parsing error") print(exn) exn.save() except BadAuth: logger.error("Bad username or password. Forgetting password.") session.forget_password() except Exception: logger.exception("Uncaught exception") else: grading.save('grading.json') session.save_cookies()
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))