Plom API

The Plom API is not yet properly documented and is in a state of flux. This page is autogenerated from our aiohttp route handler source code, which is the current best-effort documentation of the client-server API.

Caution

Subject to change!

Routes and server details for the Plom server.

Many of these routes have a corresponding server method to do non-HTTP stuff, but this separation is not perfect. In many cases the server bit just makes the same call to the database code in Plom database.

class plom.server.plomServer.IDHandler(plomServer)[source]

The ID Handler interfaces between the HTTP API and the server itself.

These routes handle requests related to identifying papers.

async ID_delete_all_predictions(data, request)[source]

Removes all predictions from all predictors for the identification.

Returns:

200 on success. Can fail with 400 (malformed) or 401/403 (auth trouble).

async ID_delete_machine_predictions(data, request)[source]

Deletes the machine-learning predicted IDs for all papers.

Parameters:
  • data (dict) – A (str:str) dictionary having keys user and token.

  • request (aiohttp.web_request.Request) –

Returns:

200 if successful. 400 for malformed, or 401/403 for auth trouble.

Return type:

aiohttp.web_response.Response

async ID_delete_predictions_from_predictor(data, request)[source]

Removes all predictions from a particular predictor for the identification.

Returns:

200 on success. Can fail with 400 (malformed) or 401/403 (auth trouble).

async ID_get_donotmark_images(data, request)[source]

Return the Do Not Mark page images for a specified paper number.

Responds with status 200/204/404/410.

Parameters:
  • data (dict) – A (str:str) dictionary having keys user and token.

  • request (aiohttp.web_request.Request) –

Returns:

If successful, then either status 200 is returned with a (positive length) multipart object of the images, or status 204 is returned when no images. Unsuccessful return values include:

  • HTTPBadRequest: authentication problem.

  • HTTPNotFound (404): no such paper.

  • HTTPGone (410): the paper is not scanned and has not been ID’d.

Return type:

aiohttp.web_response.Response

Note

if the paper is not fully scanned—specifically if the DNM pages are not scanned but nonetheless the paper is identified, then you won’t get 410, but rather 204. This is required to handle the case of HW uploads in which we know the student associated with the paper but there are no DNM-pages (and so the associated DNM group is unscanned).

async IDclaimThisTask(data, request)[source]

Claims this identifying task for the user.

Parameters:
  • data (dict) – A (str:str) dictionary having keys user and token.

  • request (aiohttp.web_request.Request) – PATCH /ID/tasks request object.

Returns:

Success or failure. Can be:

  • 200: success, you have claimed the task.

  • 401: authentication problem.

  • 409: someone else claimed it before you.

  • 404/410: no such paper or not scanned.

Return type:

aiohttp.web_response.Response

async IDgetClasslist()[source]

Returns the classlist to the client.

The classlist is an ordered list of dicts where each row has at least the primary key “id” and “name” and “paper_number”. It may contain other keys.

Used, for example, to fill in the student details for the searchbar autofill.

Responds with status success or HTTPNotFound.

Returns:

list of dicts as above.

Return type:

aiohttp.json_response

async IDgetDoneTasks(data, request)[source]

Responds with a list of id/name which have already been confirmed by the client.

Parameters:
  • data (dict) – A (str:str) dictionary having keys user and token.

  • request (aiohttp.web_request.Request) – GET /ID/tasks/complete request type.

Returns:

A response including a list of lists indicating information about the users who already have confirmed predictions. Each list in the response is of the format: [task_number, task_status, student_id, student_name].

Return type:

aiohttp.web_request.Request

async IDgetImage(data, request)[source]

Return the ID page image for a specified paper number.

Responds with status 200/204/404/409/410.

Parameters:
  • data (dict) – A (str:str) dictionary having keys user and token.

  • request (aiohttp.web_request.Request) –

Returns:

If successful, then either status 200 is returned with a (positive length) multipart object of the images, or status 204 is returned when no images. Unsuccessful return values include:

  • HTTPBadRequest: authentication problem.

  • HTTPNotFound (404): no such paper.

  • HTTPConflict (409): not the owner, or not manager, and someone else has the image

  • HTTPGone (410): the paper is not scanned and has not been ID’d.

Return type:

aiohttp.web_response.Response

Note

if the paper is not fully scanned—specifically if the ID pages are not scanned but nonetheless the paper is identified, then you won’t get 410, but rather 204. This is required to handle the case of HW uploads in which we know the student associated with the paper but there are no ID-pages (and so the associated ID group is unscanned).

async IDgetImageFromATest(data, request)[source]

Gets a random image to extract the bounding box corresponding to the student name and id.

The bounding box indicated on this image will be later used to extract the student ids from the other papers. Responds with status 200/401/403/404/410. Logs activity.

Parameters:
  • data (dict) – A (str:str) dictionary having keys user and token.

  • request (aiohttp.web_request.Request) – request of type GET /ID/randomImage.

Returns:

A response including a aiohttp object which includes a multipart object with the images.

Return type:

aiohttp.web_fileresponse.FileResponse

async IDgetNextTask()[source]

Responds with a code for the the next available identify task.

Note: There is no guarantee that task will still be available later but at this moment in time, no one else has claimed it

Responds with status 200/204.

Returns:

A response object with the code for the next task/paper.

Return type:

aiohttp.web_response.Response

async IDgetPredictions()[source]

Returns all predictions for the identification of each paper.

Each entry in the dict may contain multiple predictions.

Returns:

on success a dict where keys are str of papernum, values are lists of dicts with keys “student_id”, “certainty”, and “predictor”. Can fail with 400 (malformed) or 401 (auth trouble).

Return type:

aiohttp.web_json_response

async IDgetPredictionsFromPredictor(data, request)[source]

Returns predictions from a particular predictor for the identification.

Returns:

on success a dict where keys are str of papernum, values themselves dicts with keys “student_id”, “certainty”, and “predictor”. The “predictor” field is probably redundant: its what you asked for parroted back to you. Can fail with 400 (malformed) or 401 (auth trouble).

Return type:

aiohttp.web_json_response

async IDprogressCount()[source]

Send back current ID progress counts to the client.

Responds with status 200.

Returns:

A list of [all the ID’d records, all the records] in the form of ints.

Return type:

list

async IDputClasslist(data, request)[source]

Accept classlist upload.

Only “manager” can perform this action.

The classlist should be provided as list of dicts. Each row must contain “id” and “studentNumber” keys (case matters). Currently id must be a UBC-style student number, although it is anticipated this restriction will be removed in favour of an agnostic key. There can be other keys which should be homogeneous between rows (TODO: not well-specified what happens if not). These other fields will be given back if you get the classlist later.

Side effects on the server test spec file:

  • If numberToProduce is -1, value is set based on this classlist (spec is permanently altered).

If data[“force”] is True, then you can push a new classlist. This is not supported. Somethings to be aware of:

  • if you previously used numberToProduce of -1 and then pushed a classlist of length 100, then numberToProduce is now 100. If you force push a classlist of a different size, you may not have enough papers.

  • If you have produced prenamed papers then those predictions may not appear in the new classlist; nothing good will come from this.

Returns:

Success or failure. Can be:

  • 200: success.

  • 401: authentication problem.

  • 403: not manager.

  • HTTPBadRequest (400): malformed request such as missing required fields or server has no spec.

  • HTTPConflict: we already have a classlist, and force was False (the default).

  • HTTPNotAcceptable: classlist too short (see above).

Return type:

aiohttp.web_response.Response

async IDreviewID(data, request)[source]

Responds with an empty response object indicating if the review ID is possible and the document exists.

Responds with status 200/404.

Parameters:
  • data (dict) – A dictionary having the user/token in addition to the testNumber.

  • request (aiohttp.web_request.Request) – Request of type PATCH /ID/review.

Returns:

200 on success, 404 on failure (could not find).

Return type:

aiohttp.web.Response

async IdentifyPaper(data, request)[source]

Identify a paper directly without certain checks.

Only “manager” can perform this action. Typical client IDing would call IdentifyPaperTask() instead.

Returns:

Success or failure. Can be:

  • 200: success.

  • 403: not manager.

  • 404: papernum not found, or other data errors.

  • 409: student number data[“sid”] is already in use.

Return type:

aiohttp.web_response.Response

async IdentifyPaperTask(data, request)[source]

Identify a paper based on a task.

Returns:

Success or failure. Can be:

  • 200: success.

  • 403: some other user owns this task.

  • 404: papernum not found, or other data errors.

  • 409: student number data[“sid”] is already in use.

Return type:

aiohttp.web_response.Response

async id_reader_run(data, request)[source]

Runs the id digit reader on all paper ID pages.

Responds with status 200/202/205/401/403.

Parameters:
  • data (dict) – A dictionary having the user/token, cropping info and flag to ignore time stamp.

  • request (aiohttp.web_request.Request) – Request of type POST /ID/predictedID.

Returns:

Returns a response with the date and time of the machine reader run. Or responds with saying the machine reader is already running.

Return type:

aiohttp.web_response.Response

async machine_learning_predict_id(data, request)[source]

Match Runs the id digit reader (MLLAP and MLGreedy) on all paper ID pages.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) –

Returns:

Can be:

  • 200: successful, with some status text explaing what happened.

  • 401/403: authentication troubles

  • 406 (not acceptable): LAP is degenerate

  • 409 (conflict): ID reader still running

  • 412 (precondition failed) for no ID reader

Return type:

aiohttp.web_response.Response

async pre_id_paper(data, request)[source]

Set the prediction identification for a paper.

Returns:

Success or failure. Can be:

  • 200: success.

  • 401: auth

  • 403: not manager.

  • 404: papernum not found, or other data errors.

Return type:

aiohttp.web_response.Response

async remove_pre_id(data, request)[source]

Remove the “prename” prediction identification for a paper.

Only “manager” can perform this action. Typical client IDing would call func:IdentifyPaperTask instead.

Returns:

Success or failure. Can be:

  • 200: success.

  • 403: not manager.

  • 404: papernum not found, or other data errors.

Return type:

aiohttp.web_response.Response

setUpRoutes(router)[source]

Adds the response functions to the router object.

Parameters:

router (aiohttp.web_urldispatcher.UrlDispatcher) – Router object which we will add the response functions to.

class plom.server.plomServer.MarkHandler(plomServer)[source]

The Mark Handler interfaces between the HTTP API and the server itself.

These routes handle requests related to marking papers.

async MclaimThisTask(data, request)[source]

Take task number in request and return the task/question’s image data as a response.

Respond with status 200/204.

Parameters:
  • data (dict) – A dictionary having the user/token and the version.

  • request (aiohttp.web_request.Request) – PATCH /MK/tasks/question code request object. This request object will include the task code.

Returns:

JSON of metadata about the images in the task with status 200, or 409 if someone else has claimed this task, or a 404 if there it not yet such a task (not scanned yet) or 410 if there will never be such a task, or 400/401 for other or authentication problems. Also 417 when the version requested does not match the version of the task.

Return type:

aiohttp.json_response

async MgetAllMax()[source]

Respond with information on max mark possible for each question in the exam.

Respond with status 200/404.

Returns:

A response which includes a dictionary for the highest mark possible for each question of the exam.

Return type:

aiohttp.web_response.Response

async MgetDoneTasks(data, request)[source]

Retrieve data for questions which have already been graded by the user.

Respond with status 200.

Parameters:
  • data (dict) – Dictionary including user data in addition to question number and test version.

  • request (aiohttp.web_response.Response) – GET /MK/tasks/complete request object.

Returns:

A response object including a list of lists with the already processed questions. The list involves the question string, question mark, time spent grading and list of tag-texts.

Return type:

aiohttp.web_response.Response

async MgetNextTask(data, request)[source]

Respond with the next task/question’s string.

Respond with status 200/204.

Parameters:
  • data (dict) – Dictionary including user data in addition to question number and test version.

  • request (aiohttp.web_request.Request) – Request of type GET /MK/tasks/available.

Returns:

A 200 response which includes the next question’s string, e.g., q0013g1. If no more available, return 204.

Return type:

aiohttp.web_response.Response

async MgetOneImage(data, request)[source]

Return one image from the database.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) –

Returns:

the binary image data, or a 404 response if no such image, or a 409 if wrong md5sum sanity check was provided.

Return type:

aiohttp.web_response.Response

async MgetQuestionMark(data, request)[source]

Retrieve the maximum mark for a question.

Respond with status 200/416.

Parameters:
  • data (dict) – Dictionary including user data

  • request (aiohttp.web_request.Request) –

Returns:

JSON with the maximum mark for the question and version. Or 416 if question/version values out of range. Or BadRequest (400) if question/version cannot be converted to integers.

Return type:

aiohttp.web_response.Response

async MlatexFragment(data, request)[source]

Return the latex image for the string included in the request.

Respond with status 200/406.

Parameters:
  • data (dict) – Includes the user/token and latex string fragment.

  • request (aiohttp.web_request.Request) – Request of type GET /MK/latex.

Returns:

A response which includes the image for the latex string.

Return type:

aiohttp.web_fileresponse.FileResponse

async MprogressCount(data, request)[source]

Respond with the number of marked questions and the total questions tasks for user.

Respond with status 200.

Parameters:
  • data (dict) – Dictionary including user data in addition to question number and test version.

  • request (aiohttp.web_request.Request) – Request of type GET /MK/progress.

Returns:

Includes the number of marked tasks and the total number of marked/unmarked tasks. 400 error if q or v cannot be converted to int. 416 error if q or v are out of range.

Return type:

aiohttp.web_response.Response

async MreturnMarkedTask(request)[source]

Save the graded/processes task, extract data and save to database.

This function also responds with the number of done tasks and the total number of tasks. The returned statement is similar to MprogressCount. Respond with status 200/400/401/406/409/410. Log activity.

Parameters:

request (aiohttp.web_request.Request) – Request of type PUT /MK/tasks/question code which includes a multipart object indication the marked test data. This request will include 3 parts including [metadata, image, plom-file]. image must the the bytes of a png or jpeg file, although other formats might be accepted in the future.

Returns:

Responses with a list including the number of graded tasks and the overall number of tasks.

Return type:

aiohttp.web_response.Response

async MreviewQuestion(data, request)[source]

Confirm the question review done on plom-manager.

Parameters:
  • data (dict) – Dictionary including user data, paper_number (int) and question (int).

  • request (aiohttp.web_request.Request) – Request of type PATCH /MK/review .

Returns:

200 on success, 404 on failure (could not find), 409 if no reviewer user.

Return type:

aiohttp.web.Response

async add_tag(data, request)[source]

Add a tag for a task.

Respond with status 200/406/410.

Parameters:

data (dict) – user, token and the text for the given tag (str).

Returns:

200/204 on success, 200 for tag added and 204 indicates it was already there. HTTPNotAcceptable (406) if tag is invalid. HTTPGone (410) if cannot find task. HTTPBadRequest (400) something else went wrong.

Return type:

aiohttp.web_response.Response

async create_new_tag(data, request)[source]

Add a new tag to the system (but don’t tag anything in particular with it).

Respond with status 200/406/409.

Parameters:

data (dict) – user, token, tag_text.

Returns:

200 with key for new tag or HTTPNotAcceptable if tag text is not acceptable or HTTPConflict if tag already in system.

Return type:

aiohttp.web_response.Response

async get_all_tags(data, request)[source]

Get list of all tags in system.

Respond with status 200.

Parameters:

data (dict) – user, token.

Returns:

200 with list of tags each encoded as (key, text)

Return type:

aiohttp.web_response.Response

async get_annotations(data, request)[source]

Get the annotations of a marked question as JSON.

Parameters:
  • data (dict) – A dictionary having the user/token, and integrity which is a checksum that can be used to check that the server hasn’t changed state (for example added new scans to this question. Pass the empty string “” to omit such checks.

  • request (aiohttp.web_request.Request) – A GET request with url “/annotations/{number}/{question}/{edition}”. number and question identify which question. edition can be used to get a particular annotation from the history of all annotations. If edition is omitted, return the latest annotations.

Returns:

JSON of the annotations with status 200, or a 404 if no such image, or 400/401 for authentication problems.

Return type:

aiohttp.json_response.Response

Note: if you want the annotated image corresponding to these annotations, extract the edition from the JSON, then call “GET:/annotations_image/…” with that edition.

Ownership: note that you need not be the “owner” of this task. Getting data back from this function does not imply permission to submit to this task.

async get_annotations_img(data, request)[source]

Get the image of an annotated question (a marked question).

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – A GET request with url “/annotations_image/{number}/{question}/{edition}” number and question identify which question we want. edition can be used to get a particular annotation from the history of all annotations. If edition is omitted, return the latest annotated image.

Returns:

the binary image data with status 200, or a 404 if no such image, or 400/401 for authentication problems.

Return type:

aiohttp.web_response.Response

Note: if you want both the latest annotated image and the latest annotations (in .plom format), do not simply omit the edition in both calls: someone might upload a new annotation between your calls! Instead, call “GET:/annotations/…” first (without edition), then extract the edition from the .plom data. Finally, call this with that edition.

Ownership: note that you need not be the “owner” of this task. Getting data back from this function does not imply permission to submit to this task.

async get_annotations_img_latest(data, request)[source]

Get the image of an annotated question (a marked question).

See get_annotations_img().

async get_annotations_latest(data, request)[source]

Get the annotations of a marked question as JSON.

See get_annotations().

async get_pagedata(data, request)[source]

Return the metadata for all images associated with a paper

Respond with status 200/409.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) –

Returns:

JSON data, a list of dicts where each dict has keys: pagename, md5, included, order, id, orientation, server_path as documented below. A 409 is returned with an explanation if paper number not found.

Return type:

aiohttp.web_response.Response

The list of dicts (we think of them as rows) have the following contents:

pagename

A string something like “t2”. Reasonable to use as a thumbnail label for the image or in other cases where a very short string label is required.

md5

A string of the md5sum of the image.

id

an integer like 19. This is the key in the database to the image of this page. It is (I think) possible to have two pages pointing to the same image, in which case the md5 and the id could be repeated. TODO: determine if this only happens b/c of bugs/upload issues or if its a reasonably normal state.

order

None or an integer specifying the relative ordering of pages within a question. As with included, this information only reflects the initial (typically scan-time) ordering of the images. If its None, server has no info about what order might be appropriate, for example because this image is not thought to belong in question.

orientation

relative to the natural orientation of the image. This is an integer for the degrees of rotation. Probably only multiples of 90 work and perhaps only [0, 90, 180, 270] but could/should (TODO) be generalized for arbitrary rotations. This should be applied after any metadata rotations from inside the file instead (such as jpeg exif orientation). As with included and order, this is only the initial state. Clients may rotate images and that information belongs their annotation.

server_path

a string of a path and filename where the server might have the file stored, such as “pages/originalPages/t0004p02v1.86784dd1.png”. This is guaranteed unique (such as by the random bit before .png). It is not guaranteed that the server actually stores the file in this location, although the current implementation does.

Example:

[
  {'pagename': 't2',
   'md5': 'e4e131f476bfd364052f2e1d866533ea',
   'order': None,
   'id': 19',
   'orientation': 0
   'server_path': 'pages/originalPages/t0004p02v1.86784dd1.png',
  },
  {'pagename': 't3',
   'md5': 'a896cb05f2616cb101df175a94c2ef95',
   'order': 1,
   'id': 20,
   'orientation': 270
   'server_path': 'pages/originalPages/t0004p03v2.ef7f9754.png',
  }
]
async get_pagedata_context_question(data, request)[source]

Metadata for all non-ID images associated with a paper, highlighting those initially related to a question.

Respond with status 200/404.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) –

Returns:

JSON data, a list of dicts where each dict has keys: pagename, md5, included, order, id, orientation, server_path as documented below. A 409 is returned with an explanation if paper number not found.

Return type:

aiohttp.web_response.Response

The list of dicts (we think of them as rows) have the same content as documented in get_pagedata except an additional key:

included

boolean, did the server originally have this page included in question number question?. Note that clients may pull other pages into their annotating; you can only rely on this information for initializing a new annotating session. If you’re e.g., editing an existing annotation, you should rely on the info from that existing annotation instead of this.

Example:

[
  {'pagename': 't2',
   'md5': 'e4e131f476bfd364052f2e1d866533ea',
   'included': False,
   'order': None,
   'id': 19',
   'orientation': 0
   'server_path': 'pages/originalPages/t0004p02v1.86784dd1.png',
  },
  {'pagename': 't3',
   'md5': 'a896cb05f2616cb101df175a94c2ef95',
   'included': True,
   'order': 1,
   'id': 20,
   'orientation': 270
   'server_path': 'pages/originalPages/t0004p03v2.ef7f9754.png',
  }
]
async get_pagedata_question(data, request)[source]

Return the metadata for all images associated with a paper

Respond with status 200/409.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) –

Returns:

JSON data, a list of dicts where each dict has keys: pagename, md5, included, order, id, orientation, server_path as documented in get_pagedata(). A 409 is returned with an explanation if paper number not found.

Return type:

aiohttp.web_response.Response

async get_tags(data, request)[source]

List the tags for a task.

Parameters:

data (dict) – user, token.

Returns:

list of strings, one for each tag, or HTTPConflict (409) if user not permitted to get tags for that paper.

Return type:

aiohttp.web_response.json_response

async get_tags_of_task(data, request)[source]

List the tags for a task.

Parameters:

data (dict) – user, token.

Returns:

list of strings, one for each tag text.

Return type:

aiohttp.web_response.json_response

async remove_tag(data, request)[source]

Remove a tag from a task.

Respond with status 200/410.

Parameters:
  • data (dict) – user, token and the text of the tag (str).

  • request (aiohttp.Request) – type GET /tags/{task} where task a string like q0013g1, for paper 13 question 1.

Returns:

200 on successful removal, 204 if the task or system had no such tag, 409 if no such task,

Return type:

aiohttp.web_response.Response

setUpRoutes(router)[source]

Adds the response functions to the router object.

Parameters:

router (aiohttp.web_urldispatcher.UrlDispatcher) – Router object which we will add the response functions to.

class plom.server.plomServer.ReportHandler(plomServer)[source]

The Report Handler interfaces between the HTTP API and the server itself.

These routes handle requests related to reporting such as information requests about progress and late-stage actions such as reassembly. Typically, these are manager-only calls.

async RgetCompletionStatus(data, request)[source]

Respond with a status of the complete papers providing information on grading progress.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/completionStatus.

Returns:

a dictionary keyed by test number (str), where the values are a 3-list: [is_scanned, is_identified, number_of_questions_marked].

Return type:

aiohttp.web_response.Response

async RgetIDReview(data, request)[source]

Respond with metadata about identified papers.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/idReview.

Returns:

A response including metadata about the identified papers queued for reviewing.

Return type:

aiohttp.web_response.Response

async RgetIdentified(data, request)[source]

Respond with a dictionary of identified papers

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – GET /REP/identified request type.

Returns:

A response object including a dictionary of identified papers.

Return type:

aiohttp.web_response.Response

async RgetIncompleteTests(data, request)[source]

Respond with the incomplete exams, providing information on individual pages.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/incomplete.

Returns:

A response which includes a dictionary of pages for incomplete exams.

Return type:

aiohttp.web_response.Response

async RgetMarkHistogram(data, request)[source]

Returns histogram info for the grading of a question.

Responds with status 200/401.

Parameters:
  • data (dict) – Dictionary including user data in addition to question number.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/markHistogram.

Returns:

A response object with the grading histogram info for a question.

Return type:

aiohttp.web_response.Response

async RgetMarkReview(data, request)[source]

Respond with a list of graded tasks that match the filter description.

Parameters:
  • data (dict) – A dictionary which includes the user data in addition to the filter query information sent by the client.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/markReview.

Returns:

JSON of a list of lists of the form [Test number, Question number, Version number, Mark, Username, seconds spent marking, date/time of marking, tags]. For example: [[3, 1, 1, 5, 'user0', 7, '20:06:21-01:21:56', ''], [...]]. Can fail with 401/403 for authentication problems.

Return type:

aiohttp.web_response.Response

async RgetNotAutoIdentified(data, request)[source]

Respond with a dictionary of scanned but not auto-id’d papers

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – GET /REP/unidentified request type.

Returns:

A response object including a dictionary of scanned but not auto-id’d papers.

Return type:

aiohttp.web_response.Response

async RgetOutToDo(data, request)[source]

Respond with a list of tasks that are currently out with clients.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – GET /REP/outToDo type request.

Returns:

A response that includes a list of todo tasks.

Return type:

aiohttp.web_response.Response

async RgetProgress(data, request)[source]

Respond with an overall progress status of the marking process.

Responds with status 200/401.

Parameters:
  • data (dict) – Dictionary including user data in addition to question number and version.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/progress.

Returns:

A response which includes a dictionary of pages for incomplete exams.

Return type:

aiohttp.web_response.Response

async RgetQuestionUserProgress(data, request)[source]

Respond with information on each user’s progress on grading of a question version.

Responds with status 200/401.

Parameters:
  • data (dict) – Dictionary including user data in addition to question number and version.

  • request (aiohttp.web_request.Request) – Request GET /REP/questionUserProgress.

Returns:

A response with information on the progress of each use on each question.

Return type:

aiohttp.web_response.Response

async RgetScannedTests(data, request)[source]

Respond with a dictionary of completed exams.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/scanned.

Return type:

aiohttp.web_response.Response

async RgetSpreadsheet(data, request)[source]

Information used to create a spreadsheet during or post-grading.

Responds with status 200/401/403.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/spreadsheet.

Returns:

Information needed to build the grading results spreadsheet. The result is a large dict, keyed by paper number (integer but here a string). The value for each paper is another dict:

{
    "1": {
       'identified': True,
       'marked': False,
       'sid': '12345678',
       'sname': 'Fink, Iris',
       'q1v': 2, 'q1m': 3,
       'q2v': 1, 'q2m': '',
       'q3v': 2, 'q3m': '',
       'last_update': '2022-05-13T21:15:02.072122+00:00'
    },
    "2": {
       ...
    }
}

Notable here is q1v which is “Question 1 version” (an integer), and q1m which is “Question 1 mark”, an integer or the empty string if the question is still being marked (marked should be False in this case).

Return type:

aiohttp.web_response.Response

async RgetStatus(data, request)[source]

Respond with the marking status of an exam.

Responds with status 200/401/404.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of typeGET /REP/status/2 which also includes the test number.

Returns:

A response including a dictionary for information on grading status for an exam.

Return type:

aiohttp.web_response.Response

async RgetUserDetails(data, request)[source]

Gets a list of users and their detail.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request GET /REP/userDetails.

Returns:

A response object entailing a list of plom users.

Return type:

aiohttp.web_response.Response

async RgetUserList(data, request)[source]

Return a list of Plom users.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/userList.

Returns:

A response object entailing a list of Plom users.

Return type:

aiohttp.web_response.Response

async getDanglingPages(data, request)[source]

Respond with the list of dangling pages - pages attached to groups that are not complete.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/incomplete.

Returns:

A response which includes a list of dictionaries of pages that belong to incomplete groups (ie not completely scanned, and not ID’d or marked)

Return type:

aiohttp.web_response.Response

async getFilesInAllTests(data, request)[source]

Respond with metadata about image-files used in all tests.

In particular, for each test, which imagefiles/bundles are used for each id-group, dnm-group, and question-group.

Responds with status 200/401/403.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/idReview.

Returns:

A response including metadata about the files used.

Return type:

aiohttp.web_response.Response

setUpRoutes(router)[source]

Adds the response functions to the router object.

Parameters:

router (aiohttp.web_urldispatcher.UrlDispatcher) – Router object which we will add the response functions to.

class plom.server.plomServer.RubricHandler(plomServer)[source]

The Rubric Handler interfaces between the HTTP API and the server itself.

These routes handle requests related to rubrics.

async McreateRubric(data, request)[source]

Respond with updated comment list and add received comments to the database.

Parameters:
  • data (dict) – A dictionary including user/token and the new rubric to be created

  • request (aiohttp.web_request.Request) – A request of type PUT /MK/rubric.

Returns:

either 200 with the new key or 406 if sent rubric was incomplete.

Return type:

aiohttp.web_response.Response

async MgetRubrics(data, request)[source]

Respond with the current comment list.

Parameters:
  • data (dict) – A dictionary including user/token

  • request (aiohttp.web_request.Request) – A request of type GET /MK/rubric.

Returns:

List of all comments in DB

Return type:

aiohttp.web_response.Response

async MgetRubricsByQuestion(data, request)[source]

Respond with the comment list for a particular question.

Parameters:
  • data (dict) – A dictionary including user/token

  • request (aiohttp.web_request.Request) – A request of type GET /MK/rubric/{question}.

Returns:

List of all comments in DB

Return type:

aiohttp.web_response.Response

async MgetUserRubricPanes(data, request)[source]

Get user’s rubric-panes configuration from server

Parameters:
  • data (dict) – A dictionary including user/token and question number.

  • request (aiohttp.web_request.Request) – GET /MK/user/{user}/{question}.

Returns:

success and the config (as json), or 204 if nothing available. Error responses:

  • HTTPUnauthorized

  • HTTPBadRequest: inconsistent question or missing fields.

  • HTTPForbidden: trying to save to another user.

Return type:

aiohttp.web_response.Response

async MmodifyRubric(data, request)[source]

Add modify rubric to DB and respond with its key

Parameters:
  • data (dict) – A dictionary including user/token and the new rubric to be created

  • request (aiohttp.web_request.Request) – A request of type GET /MK/rubric.

Returns:

either 200 with the key or 406 if sent rubric was incomplete or inconsistent, 409 if no rubric found, or some unexpected situation.

Return type:

aiohttp.web_response.Response

async MsaveUserRubricPanes(data, request)[source]

Add new rubric to DB and respond with its key

Parameters:
  • data (dict) – A dictionary including user/token and a blob of data to save for the user’s rubric tab setup.

  • request (aiohttp.web_request.Request) – PUT /MK/user/{user}/{question}.

Returns:

200 on success or

  • HTTPUnauthorized

  • HTTPBadRequest: inconsistent question or missing fields.

  • HTTPForbidden: trying to save to another user.

Return type:

aiohttp.web_response.Response

async RgetRubricCounts(data, request)[source]

Respond with dict encoding rubric counts and other minimal info.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/rubric/counts.

Returns:

A response including metadata encoding the rubric counts and min info. Returns a list of rubrics, and for each rubric we give a dict listing its id, kind, question, delta, text, user who created it, and the count of how many tests it has been used in.

Return type:

aiohttp.web_response.Response

async RgetRubricDetails(data, request)[source]

Respond with dict encoding rubric counts and other minimal info.

Responds with status 200/401/BadRequest.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/rubric/key.

Returns:

A response including metadata encoding the rubric details inc which tests use it. More precisely, we return a dict that gives the rurbrics id, kind, question, delta, text, who created it, tags, meta, count, creation and modification times, and a list of test numbers in which it was used.

Return type:

aiohttp.web_response.Response

async RgetTestRubricMatrix(data, request)[source]

Respond with dict encoding test-rubric counts.

Responds with status 200/401.

Parameters:
  • data (dict) – A dictionary having the user/token.

  • request (aiohttp.web_request.Request) – Request of type GET /REP/test_rubric_adjacency.

Returns:

A response including metadata encoding the test-rubric adjacency / count matrix. The matrix is encoded as an adjacency list, i.e., {testnumber: [rubric_id1, rubric_id2, ...]} where (test_n, rubric_k) means that rubric_k was used in test_n.

Return type:

aiohttp.web_response.Response

setUpRoutes(router)[source]

Adds the response functions to the router object.

Parameters:

router (aiohttp.web_urldispatcher.UrlDispatcher) – Router object which we will add the response functions to.

validateRubric(username, rubric)[source]

Do some simple validation of the rubric

Parameters:
  • username (str) – the name of the user trying to create the rubric

  • rubric (dict) – a dict containing the rubric info

Returns:

true if valid, false otherwise.

Return type:

bool

class plom.server.plomServer.SolutionHandler(plomServer)[source]

The Solution Handler interfaces between the HTTP API and the server itself.

These routes handle requests related to solutions.

setUpRoutes(router)[source]

Adds the response functions to the router object.

Parameters:

router (aiohttp.web_urldispatcher.UrlDispatcher) – Router object which we will add the response functions to.

class plom.server.plomServer.UploadHandler(plomServer)[source]

The Upload Handler interfaces between the HTTP API and the server itself.

These routes handle requests related to uploading of images, such as at scanning time. Also included are various administrative actions such as shifting UnknownPages into ExtraPages.

Various miscellaneous routes about initialising and configuring the server seem to have landed here as well.

async appendTestToExamDatabase(data, request)[source]

Append given test to database using given version map.

Returns:

200 on success and a status message summarizing the newly created row. Errors:

  • 400 for server does not have spec.

  • 401 for authentication, or 403 if not manager.

  • 406 (unacceptable) for problems with version map or spec.

  • 409 (conflict) for row already exists or otherwise cannot be created.

  • 500 for unexpected errors.

Return type:

web.Response

async collidingToTestPage(request)[source]

The group containing the tpage is reset when it is replaced.

At the same time, any annotation that involved the old tpage is reset.

async createNewBundle(request)[source]

Try to create bundle with given name/md5sum.

First check name / md5sum of bundle.

  • If bundle matches either ‘name’ or ‘md5sum’ then return [False, reason] - this shouldn’t happen if scanner working correctly.

  • If bundle matches ‘both’ then return [True, skip_list] where skip_list = the page-orders from that bundle that are already in the system. The scan scripts will then skip those uploads.

  • If no such bundle return [True, []] - create the bundle and return an empty skip-list.

Note

  • after declaring a bundle you may upload images to it.

  • uploading pages to an undeclared bundle is not allowed.

  • bundles traditionally correspond to one “pile” of physical papers scanned together.

  • there does not need to be one-to-one relationship betewen bundles and Exam Papers or Homework Papers.

async doesBundleExist(request)[source]

Returns whether given bundle/md5sum known to database

Checks both bundle’s name and md5sum

  • neither = no matching bundle, return [False, None]

  • name but not md5 = return [True, ‘name’] - user is trying to upload different bundles with same name.

  • md5 but not name = return [True, ‘md5sum’] - user is trying to same bundle with different names.

  • both match = return [True, ‘both’] - user could be retrying after network failure (for example) or uploading unknown or colliding pages.

async getBundleFromImage(data, request)[source]

Returns the name of the bundle that contains the given image.

If DB can’t find the file then returns HTTPGone error. If not manager, then raise an HTTPUnauthorized error.

async getGlobalPageVersionMap(data, request)[source]

Get the mapping between page number and version for all tests.

Returns:

dict of dicts, keyed first by paper index then by page number. Both keys are strings b/c of json limitations; you may need to iterate and convert back to int. Fails with 409 if the version map database has not been built yet.

Return type:

dict

Caution

careful not to confuse this with /plom/admin/questionVersionMap which is much more likely what you are looking for.

async getGlobalQuestionVersionMap(data, request)[source]

Get the mapping between question and version for all tests.

Returns:

dict of dicts, keyed first by paper index then by question number. Both keys will become strings b/c of json limitations; you may need to convert back to int. If the server does not yet have any database, the version map will be empty.

Return type:

dict

async getImagesInBundle(data, request)[source]

Returns list of images inside the given bundle. Each image is returned as a triple of (filename, md5sum and bundle_order). The list is ordered by the bundle_order.

If DB does not contain bundle of that name a 410-error returned. If user is not manager or scanner then a HTTPUnauthorised error raised.

async getPageFromBundle(request)[source]

Get the image at position bundle_order from the bundle with the given name. This is used (for example) to examine neighbouring images inside a given bundle.

If DB does not contain a bundle of that name or the bundle does not contain an image at that order then raise an HTTPGone error.

async getQuestionVersionMap(data, request)[source]

Get the mapping between questions and version for one test.

Returns:

keyed by question number. Note keys will be strings b/c of json limitations; you may need to convert back to int. Fails with 409 if there is no such paper.

Return type:

dict

async initialiseExamDatabase(data, request)[source]

Instruct the server to generate paper data in the database.

async listBundles(request)[source]

Returns a list of dicts of bundles in the database.

async removeSinglePage(data, request)[source]

Remove the page (as described by its name) and reset any tasks that involve that page.

This tries to be as minimal as possible - so, for example, if a tpage is removed, then the question that included that page goes back on the todo-list (after a newpage is uploaded), but at the same time if a TA has used a copy of that page in the annotation of another question, that group is also reset and goes back on the todo-list.

async removeUnknownImage(data, request)[source]

The unknown page is to be discarded.

Parameters:

request (aiohttp.web_request.Request) –

This has the usual “user” and “token” fields but also:

  • fileName (str): identifies the UnknownPage.

  • reason (str): a short reason why which could be canned or free-form from the user. If the empty string, then a default message may be substituted.

Returns:

200 if all went well. 400 for incorrect fields, 401 for authentication, or 403 is not manager. 404 if there isn’t any such image, or it is not a UnknownImage, with details given in the reason.

Return type:

web.Response

async replaceMissingDNMPage(data, request)[source]

Replace a do-not-mark page with a server-generated placeholder.

We will create the placeholder image.

Parameters:

dict. (test and page in the data) –

Returns:

on success, currently with some json diagnostics info. 401/403: auth. 404: page or test not found. 409: conflict such as collision with image already in place or a repeated upload of the same placeholder. 400: poorly formed or otherwise unexpected catchall.

Return type:

200

async replaceMissingIDPage(data, request)[source]

Replace a missing ID page a server-generated placeholder, for identified tests only.

TODO: suspicious quality, needs work, Issue #2461.

Parameters:

dict. (test in the data) –

Returns:

on success, currently with some json diagnostics info (?) but can also return 200 when HW already had an ID page (?) 401/403: auth 404: page or test not found 410: paper not identified (e.g., not homework) 400: poorly formed or otherwise unexpected catchall

Return type:

200

async replaceMissingTestPage(data, request)[source]

Replace a do-not-mark page with a server-generated placeholder.

We will create the placeholder image.

Parameters:
  • test

  • page

  • dict. (and version in the data) –

Returns:

on success, currently with some json diagnostics info. 401/403: auth. 404: page or test not found. 409: conflict such as collision with image already in place or a repeated upload of the same placeholder. 400: poorly formed or otherwise unexpected catchall.

Return type:

200

async sidToTest(request)[source]

Match given student_id to a test-number.

Returns:

depend on success or failure, gives:

  • [True, test_number]

  • [False, "Cannot find test with that student id"]

Return type:

list

The test number could be b/c the paper is IDed. Or it could be a prediction (a confident one, currently “prename”).

async unknownToExtraPage(data, request)[source]

Map an unknown page onto one or more extra pages.

Parameters:

request (aiohttp.web_request.Request) –

This has the usual “user” and “token” fields but also:

  • fileName (str): identifies the UnknownPage.

  • test (str): paper number to map onto (int passed as str).

  • questions (list): question numbers, a list of integers.

  • rotation (str): an integer, presumably a multiple of 90 0, 90, -90, 180, 270, etc.

Returns:

200 if all went well. 400 for incorrect fields, 401 for authentication, or 403 is not manager. 409 if paper number or question number do not exist (e.g., out of range). Also, 409 if one or more questions not scanned (so cannot attach extra page). This is important as otherwise we can bypass the scanned mechanism and a test of only extra pages could be overlooked (not graded nor returned).

Return type:

web.Response

async unknownToHWPage(data, request)[source]

Map an unknown page onto one or more HomeworkPages.

Parameters:

request (aiohttp.web_request.Request) –

This has the usual “user” and “token” fields but also:

  • fileName (str): identifies the UnknownPage.

  • test (str): paper number to map onto (int passed as str).

  • questions (list): question numbers, ints.

  • rotation (str): an integer, presumably a multiple of 90 0, 90, -90, 180, 270, etc.

Returns:

200 if all went well. 400 for incorrect fields, 401 for authentication, or 403 is not manager. 409 if paper number or question number do not exist (e.g., out of range).

Return type:

web.Response

async unknownToTestPage(data, request)[source]

The unknown page is moved to the indicated tpage.

The minimal set of groups are reset when this happens - namely the group containing the new tpage.

Parameters:

request (aiohttp.web_request.Request) –

This has the usual “user” and “token” fields but also:

  • fileName (str): identifies the UnknownPage.

  • test (str): paper number to map onto (int passed as str).

  • page (str): page number (again, an int)

  • rotation (str): an integer, presumably a multiple of 90 0, 90, -90, 180, 270, etc.

Returns:

200 if all went well, with a string in JSON which can be “collision” if a collision was created or “testPage” in the usual successful case. 400 for incorrect fields, 401 for authentication, or 403 is not manager. 409 in one of various “not found” situations, such as test number or page number do not exist.

Return type:

web.Response

async uploadHWPage(request)[source]

A homework page is self-scanned, known student, and known(-ish) questions.

Typically the page is without QR codes. The uploader knows what student it belongs to and what question(s). The order within the question is somewhat known too, at least within its upload bundle.

Parameters:

request (aiohttp.web_request.Request) – a multipart thing The questions field is a list of questions.

Returns:

JSON data directly from the database call.

Return type:

aiohttp.web_response.Response

Note: this uses the status=200 success return code for some kinds of failures: it simply returns whatever data the DB gave back as blob of json for the client to deal with. Thus, this API call is not recommended outside of Plom.

async uploadTestPage(request)[source]

A test page has known page, known paper number, usually QR-coded.

Typically the page is QR coded, and thus we know precisely what paper number, what question and what page. We may not know the student depending on whether it was prenamed or not.

Parameters:

request (aiohttp.web_request.Request) –

Returns:

JSON data directly from the database call.

Return type:

aiohttp.web_response.Response

Note: this uses the status=200 success return code for some kinds of failures: it simply returns whatever data the DB gave back as blob of json for the client to deal with. Thus, this API call is not recommended outside of Plom.

class plom.server.plomServer.UserInitHandler(plomServer)[source]

The UserInit Handler interfaces between the HTTP API and the server itself.

These routes handle requests related to administration of user accounts.

InfoShortName(request)[source]

The short name of the exam.

Returns:

200 and the short name or 400 if the server has no spec.

Return type:

aiohttp.Response

async changeUserPassword(data, request)[source]

Change an existing user’s password.

async closeUser(data, request)[source]

User self-indicates they are logging out, revoke token and tasks.

Returns:

200 for success, unless a user tries to close another which is BadRequest (400) or user is already logged out, or nonexistent, both of which will give an Unauthorized (401).

Return type:

aiohttp.web.Response

async createUser(data, request)[source]

Create a new user.

info_spec(request)[source]

Return the public part of the server specification.

Returns:

  • 200: the public part of the spec.

  • 400: spec not found (server does not have one yet).

Return type:

aiohttp.json_response

async put_spec(data, request)[source]

Accept an uploaded exam specification.

Returns:

  • 403: only manager can upload a spec.

  • 400: the provided spec is not valid.

  • 409: Conflict: server has already initialised or populated the database.

  • 200: new spec file accepted. TODO: would be polite to inform caller if we already had one or not.

Return type:

aiohttp.Response