Example #1
0
 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
Example #2
0
    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()
Example #3
0
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))
Example #4
0
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))