def parse_thread_posts(document): post_elements = document.findall('.//h:div[@class="dbThread"]', NS) h_dt = '{%s}dt' % NS['h'] h_dd = '{%s}dd' % NS['h'] for post in post_elements: checkbox = post.find('.//h:input[@type="checkbox"][@name="formCBs"]', NS) message_id = checkbox.get('value') message_title = checkbox.get('title') data = [] for dl in post.findall('.//h:dl', NS): key = None for c in dl: text = element_text_content(c) if c.tag == h_dt: key = text elif c.tag == h_dd: data.append((key, text)) body = post.find('.//h:div[@class="dbThreadBody"]', NS) if body is not None: body = element_to_markdown(body) else: body = '' yield dict(message_id=message_id, message_title=message_title, metadata=data, body=body)
def parse_thread_posts(document): post_elements = document.findall('.//h:div[@class="dbThread"]', NS) h_dt = '{%s}dt' % NS['h'] h_dd = '{%s}dd' % NS['h'] for post in post_elements: checkbox = post.find( './/h:input[@type="checkbox"][@name="formCBs"]', NS) message_id = checkbox.get('value') message_title = checkbox.get('title') data = [] for dl in post.findall('.//h:dl', NS): key = None for c in dl: text = element_text_content(c) if c.tag == h_dt: key = text elif c.tag == h_dd: data.append((key, text)) body = post.find('.//h:div[@class="dbThreadBody"]', NS) if body is not None: body = element_to_markdown(body) else: body = '' yield dict( message_id=message_id, message_title=message_title, metadata=data, body=body)
def fetch_attempt(session, attempt_id, is_group_assignment): 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) l = blackboard.slowlog() response = session.get(url) l("Fetching attempt took %.1f s") document = html5lib.parse(response.content, encoding=response.encoding) submission_text = document.find( './/h:div[@id="submissionTextView"]', NS) if submission_text is not None: submission_text = element_to_markdown(submission_text) comments = document.find( './/h:div[@id="currentAttempt_comments"]', NS) if comments is not None: xpath = './/h:div[@class="vtbegenerated"]' comments = [ element_to_markdown(e) for e in comments.findall(xpath, NS) ] if not comments: raise blackboard.ParserError( "Page contains currentAttempt_comments, " + "but it contains no comments", response) comments = '\n\n'.join(comments) files = [] submission_list = document.find( './/h:ul[@id="currentAttempt_submissionList"]', NS) if submission_list is None: raise ParserError("No currentAttempt_submissionList", response) for submission in submission_list: filename = element_text_content(submission) download_button = submission.find( './/h:a[@class="dwnldBtn"]', NS) if download_button is not None: download_link = urljoin( response.url, download_button.get('href')) files.append( dict(filename=filename, download_link=download_link)) else: s = 'currentAttempt_attemptFilesubmissionText' a = submission.find( './/h:a[@id="' + s + '"]', NS) if a is not None: # This <li> is for the submission_text if not submission_text: raise blackboard.ParserError( "%r in file list, but no " % (filename,) + "accompanying submission text contents", response) else: raise blackboard.ParserError( "No download link for file %r" % (filename,), response) score_input = document.find( './/h:input[@id="currentAttempt_grade"]', NS) if score_input is None: score = None else: score = form_field_value(score_input) try: score = float(score) except ValueError: if score: raise blackboard.ParserError( "Couldn't parse currentAttempt_grade: %r" % (score,), response) score = None feedbacktext_input = document.find( './/*[@id="feedbacktext"]', NS) if feedbacktext_input is None: feedback = '' else: feedback = form_field_value(feedbacktext_input) if '<' in feedback: feedback = html_to_markdown(feedback) gradingNotestext_input = document.find( './/*[@id="gradingNotestext"]', NS) if gradingNotestext_input is None: grading_notes = '' else: grading_notes = form_field_value(gradingNotestext_input) feedbackfiles_rows = document.find( './/h:tbody[@id="feedbackFiles_table_body"]', NS) feedbackfiles = [] for i, row in enumerate(feedbackfiles_rows or []): try: link = row.findall('.//h:a', NS)[0] except IndexError: raise blackboard.ParserError( "feedbackFiles_table_body row %s: no link" % i, response) download_link = urljoin( response.url, link.get('href')) filename = element_text_content(link) feedbackfiles.append( dict(filename=filename, download_link=download_link)) rubric_data = None if is_group_assignment: rubric_input = document.find( './/h:input[@id="%s_rubricEvaluation"]' % attempt_id, NS) if rubric_input is not None: rubric_data_str = form_field_value(rubric_input) try: rubric_data = json.loads(unquote(rubric_data_str)) except JSONDecodeError: raise ParserError("Couldn't decode JSON", response) t1 = 'blackboard.platform.gradebook2.GroupAttempt' t2 = 'blackboard.plugin.rubric.api.core.data.EvaluationEntity' if rubric_data['evalDataType'] == t1: if rubric_data['evalEntityId'] != attempt_id: raise ParserError( "evalEntityId is %r, expected %r" % (rubric_data['evalEntityId'], attempt_id), response) elif rubric_data['evalDataType'] == t2: # Seems to indicate an already filled-out rubric pass else: raise ParserError( "Unknown evalDataType %r" % rubric_data['evalDataType'], response) return dict( submission=submission_text, comments=comments, files=files, feedback=feedback, feedbackfiles=feedbackfiles, score=score, grading_notes=grading_notes, rubric_data=rubric_data, )
def fetch_attempt(session, attempt_id, is_group_assignment): assert isinstance(session, BlackboardSession) if is_group_assignment: url = ('https://%s/webapps/assignment/' % DOMAIN + 'gradeAssignmentRedirector' + '?course_id=%s' % session.course_id + '&groupAttemptId=%s' % attempt_id) else: url = ('https://%s/webapps/assignment/' % DOMAIN + 'gradeAssignmentRedirector' + '?course_id=%s' % session.course_id + '&attempt_id=%s' % attempt_id) l = blackboard.slowlog() response = session.get(url) l("Fetching attempt took %.1f s") document = html5lib.parse(response.content, transport_encoding=response.encoding) currentAttempt_container = document.find('.//h:div[@id="currentAttempt"]', NS) if currentAttempt_container is None: not_yet_submitted = ('This attempt has not yet been submitted and ' + 'is not available to view at present.') if not_yet_submitted in response.text: raise NotYetSubmitted raise blackboard.ParserError('No <div id="currentAttempt">', response=response) submission_text = document.find('.//h:div[@id="submissionTextView"]', NS) if submission_text is not None: submission_text = element_to_markdown(submission_text) comments = document.find('.//h:div[@id="currentAttempt_comments"]', NS) if comments is not None: xpath = './/h:div[@class="vtbegenerated"]' comments = [ element_to_markdown(e) for e in comments.findall(xpath, NS) ] if not comments: raise blackboard.ParserError( "Page contains currentAttempt_comments, " + "but it contains no comments", response) comments = '\n\n'.join(comments) files = [] submission_list = document.find( './/h:ul[@id="currentAttempt_submissionList"]', NS) if submission_list is None: if comments is None and submission_text is None: logger.warning("The submission is completely empty.") elif submission_text is None: logger.warning( "No submission; the student only uploaded a comment.") else: logger.warning("The student only uploaded a text submission.") submission_list = () for submission in submission_list: filename = element_text_content(submission) download_button = submission.find('.//h:a[@class="dwnldBtn"]', NS) if download_button is not None: download_link = urljoin(response.url, download_button.get('href')) files.append(dict(filename=filename, download_link=download_link)) else: s = 'currentAttempt_attemptFilesubmissionText' a = submission.find('.//h:a[@id="' + s + '"]', NS) if a is not None: # This <li> is for the submission_text if not submission_text: raise blackboard.ParserError( "%r in file list, but no " % (filename, ) + "accompanying submission text contents", response) else: raise blackboard.ParserError( "No download link for file %r" % (filename, ), response) score_input = document.find('.//h:input[@id="currentAttempt_grade"]', NS) if score_input is None: score = None else: score = form_field_value(score_input) try: score = float(score) except ValueError: if score: raise blackboard.ParserError( "Couldn't parse currentAttempt_grade: %r" % (score, ), response) score = None feedbacktext_input = document.find('.//*[@id="feedbacktext"]', NS) if feedbacktext_input is None: feedback = '' else: feedback = form_field_value(feedbacktext_input) if '<' in feedback: feedback = html_to_markdown(feedback) gradingNotestext_input = document.find('.//*[@id="gradingNotestext"]', NS) if gradingNotestext_input is None: grading_notes = '' else: grading_notes = form_field_value(gradingNotestext_input) feedbackfiles_rows = document.find( './/h:tbody[@id="feedbackFiles_table_body"]', NS) feedbackfiles = [] for i, row in enumerate(feedbackfiles_rows or []): try: link = row.findall('.//h:a', NS)[0] except IndexError: raise blackboard.ParserError( "feedbackFiles_table_body row %s: no link" % i, response) download_link = urljoin(response.url, link.get('href')) filename = element_text_content(link) feedbackfiles.append( dict(filename=filename, download_link=download_link)) rubric_data = None if is_group_assignment: rubric_input = document.find( './/h:input[@id="%s_rubricEvaluation"]' % attempt_id, NS) if rubric_input is not None: rubric_data_str = form_field_value(rubric_input) try: rubric_data = json.loads(unquote(rubric_data_str)) except JSONDecodeError: raise ParserError("Couldn't decode JSON", response) t1 = 'blackboard.platform.gradebook2.GroupAttempt' t2 = 'blackboard.plugin.rubric.api.core.data.EvaluationEntity' if rubric_data['evalDataType'] == t1: if rubric_data['evalEntityId'] != attempt_id: raise ParserError( "evalEntityId is %r, expected %r" % (rubric_data['evalEntityId'], attempt_id), response) elif rubric_data['evalDataType'] == t2: # Seems to indicate an already filled-out rubric pass else: raise ParserError( "Unknown evalDataType %r" % rubric_data['evalDataType'], response) return dict( submission=submission_text, comments=comments, files=files, feedback=feedback, feedbackfiles=feedbackfiles, score=score, grading_notes=grading_notes, rubric_data=rubric_data, )