Source code for uw.local.teaching.db.markbox

"""Markbox-specific routines for printing examinations.

Routines for generating the API request to Markbox and handling the
responses. 
"""

import json
import urllib.request, urllib.error, urllib.parse

from ll.xist.ns import html

from uw.local.config import read_config

from .pdf import embed_fonts

api_key_config = read_config ()

[docs]def format_markbox_url (exam, template): return template % (api_key_config.get ('markbox', 'request_url'), exam.markbox_exam_code)
[docs]def format_classlist_url (exam): return format_markbox_url (exam, "%s/odyssey/update-classlist/%s/")
[docs]def format_pdf_url (exam): return format_markbox_url (exam, "%s/odyssey/get-pdf/%s/")
[docs]def format_create_url (): return "%s/odyssey/create/" % api_key_config.get ('markbox', 'request_url')
[docs]def format_exam_json (cursor, exam): result = { 'term_id': exam.term_id, 'exam_id': exam.exam_id, 'exam_type': 'preprinted', 'exam_title': cursor.callproc_required_value ("exam_exam_title", exam.exam_id), 'admin_id': exam.admin_id, 'admin_description': cursor.execute_required_value ("select admin_description from teaching_admin where admin_id=%(admin_id)s", admin_id=exam.admin_id), 'exam_author_userid': cursor.execute_required_value ("select userid from person_identity_complete where person_id = %(person_id)s", person_id=exam.exam_author_person_id), } return result
[docs]def issue_create_request (cursor, exam, request_json, now): request = urllib.request.Request (format_create_url ()) request.add_header ("Content-Type", "application/vnd.api+json") request.data = (json.dumps (request_json).encode ()) request.add_header ("Authorization", "Token token=%s" % api_key_config.get ('markbox', 'request_token')) try: response = urllib.request.urlopen (request) success = True except urllib.error.HTTPError as x: response = x success = False status = response.getcode () result = response.read () cursor.execute_none ("update markbox_create_request set response_status = %(status)s, response_body = %(response)s where request_time = %(time)s and response_status is null", status=status, response=result, time=now) cursor.connection.commit () if success and status == 201: # Success - record slug in result_json = json.loads (result.decode ()) slug = str (result_json['slug']) cursor.execute_none ("update markbox_exam set markbox_exam_code = %(code)s where exam_id = %(exam_id)s", exam_id=exam.exam_id, code=slug) cursor.connection.commit () return slug else: print (result) raise response
[docs]def create_exam (cursor, exam): if not exam.scanning_integration == 'M': raise ValueError ('Assessment not configured for Markbox') now = cursor.execute_required_value ("insert into markbox_create_request (exam_id) values (%(exam_id)s) returning request_time", exam_id=exam.exam_id) cursor.connection.commit () request_json = format_exam_json (cursor, exam) cursor.execute_none ("update markbox_create_request set request_json = %(request_json)s where request_time = %(time)s and request_json is null", request_json=json.dumps (request_json), time=now) cursor.connection.commit () slug = issue_create_request (cursor, exam, request_json, now) return request_json, slug
[docs]def format_candidate_json (cursor, exam): instances = cursor.execute_tuples ("select exam_sequence, uw_id, givennames, surname, userid from exam_exam_student_sitting natural join exam_exam natural join person_identity_complete natural left join exam_exam_instance where exam_id = %(exam_id)s order by userid", exam_id=exam.exam_id) candidates = [] for instance in instances: instance_json = { 'uw_id': instance.uw_id, 'first_name': instance.givennames, 'last_name': instance.surname, 'userid': instance.userid, } if instance.exam_sequence is not None: instance_json['exam_sequence'] = instance.exam_sequence candidates.append (instance_json) return {'candidates': candidates}
[docs]def send_classlist (cursor, exam): request_json = format_candidate_json (cursor, exam) request = urllib.request.Request (format_classlist_url (exam)) request.add_header ("Content-Type", "application/vnd.api+json") request.data = (json.dumps (request_json).encode ()) request.add_header ("Authorization", "Token token=%s" % api_key_config.get ('markbox', 'request_token')) try: response = urllib.request.urlopen (request) success = True except urllib.error.HTTPError as x: response = x success = False status = response.getcode () result = response.read () if success: return status, result else: print (result) raise response
[docs]def markbox_process_pdf (cursor, exam_id): """Embed fonts in the Markbox PDF for the specified examination. :param cursor: DB cursor. :param exam_id: The exam_id of the examination to process. Obtain the raw PDF from markbox_exam.markbox_answer_page_pdf_raw, process it through the standard font embedding process, and store the result in markbox_exam.markbox_answer_page_pdf. """ pdf_raw, pdf_old = cursor.execute_required_tuple ("select markbox_answer_page_pdf_raw, markbox_answer_page_pdf from markbox_exam where exam_id = %(exam_id)s for no key update nowait", exam_id=exam_id) if pdf_raw is None: raise SystemError (["Raw PDF is NULL"]) if pdf_old is not None: raise SystemError (["Old PDF is not NULL"]) status, result = embed_fonts (pdf_raw) if not status: raise SystemError (result) cursor.execute_none ("update markbox_exam set markbox_answer_page_pdf = %(pdf)s where exam_id = %(exam_id)s", exam_id=exam_id, pdf=result) cursor.connection.commit ()
[docs]def get_pdf (cursor, exam): request = urllib.request.Request (format_pdf_url (exam)) request.add_header ("Authorization", "Token token=%s" % api_key_config.get ('markbox', 'request_token')) try: response = urllib.request.urlopen (request) success = True except urllib.error.HTTPError as x: response = x success = False status = response.getcode () result = response.read () if success and status == 200: cursor.execute_none ("update markbox_exam set markbox_answer_page_pdf_raw = %(pdf)s, markbox_answer_page_pdf = null where exam_id = %(exam_id)s", exam_id=exam.exam_id, pdf=result) cursor.connection.commit () markbox_process_pdf (cursor, exam.exam_id) else: print (result) raise response
[docs]def markbox_exam (cursor, exam): create_exam (cursor, exam) # Need to get the markbox slug for remaining steps exam = cursor.exam_by_id (exam_id=exam.exam_id) send_classlist (cursor, exam) get_pdf (cursor, exam)