plom.client module

Plom Client

Plom client and supporting functions.

class plom.client.Chooser(Qapp, webplom=False)[source]
closeEvent(self, QCloseEvent)[source]
getQuestion()[source]

Return the integer question or None

getv()[source]

Return the integer version or None

partial_parse_address()[source]

If address has a port number in it, extract and move to the port box.

If there’s a colon in the address (maybe user did not see port entry box or is pasting in a string), then try to extract a port number and put it into the entry box.

In some rare cases, we actively clear the port box, for example when the URL seems to have a path.

saveDetails()[source]

Write the options to the config file.

setFont(n)[source]

Adjust font size of user interface.

Parameters:

n (int) – the desired font size in points.

setServer(s)[source]

Set the server and port UI widgets from a string.

If port is missing, a default will be used. If we cannot parse the url, just leave it alone.

class plom.client.IDClient(Qapp, tmpdir=None)[source]

Initialize the Identifier Client.

Parameters:
  • Qapp (QApplication) – Main client application

  • tmpdir (pathlib.Path/str/None) – a temporary directory for storing image files and other data. In principle can be shared with Marker although this may not be implemented. If None, we will make our own.

closeEvent(self, QCloseEvent)[source]
enterID()[source]

Triggered when user hits return in the ID-lineedit.. that is when they have entered a full student ID.

getClassList()[source]

Get the classlist from the server.

Here and throughout ‘snid’ means “student_id_and_name” as one string.

Returns nothing but modifies the state of self, adding three dicts to the class data:

snid_to_student_id snid_to_student_name student_id_to_snid

Raises:

PlomNoClasslist

getPredictions()[source]

Send request for prediction list to server.

For some reason, this also updates font-sizes and stuff.

identifyStudent(index, sid, sname, blank=False, no_id=False)[source]

Push identification of a paper to the server and misc UI table.

User ID’s the student of the current paper. Some care around whether or not the paper was ID’d previously. Not called directly - instead is called by “enterID” or “acceptPrediction” when user hits return on the line-edit.

Parameters:
  • index – an index into the UI table of the currently highlighted row.

  • sname (str) – The student name or special placeholder. - note that this should always be non-trivial string.

  • sid (str/None) – The student ID or None. - note that this is either ‘None’ (but only if blank or no_id is true), or should have passed the ‘is_valid_id’ test.

  • blank (bool) – the paper was blank: sid must be None and sname must be “Blank paper”.

  • no_id (bool) – paper is not blank but student did not fill-in the ID page(s). sid must be None and sname must be “No ID given”.

Returns:

True on success, False/None on failure.

Return type:

True/False/None

requestNext()[source]

Ask the server for an unID’d paper. Get file, add to the list of papers and update the image.

setCompleters()[source]

Set up the studentname + studentnumber line-edit completers. Means that user can enter the first few numbers (or letters) and be prompted with little pop-up with list of possible completions.

setup(messenger)[source]

Performs setup procedure for the IDClient.

Parameters:

messenger (Messenger) – handles communication with server.

TODO: move all this into init?

shutDownError()[source]

Shuts down self due to error.

skipOnClick()[source]

Skip the current, moving to the next or loading a new one

class plom.client.MarkerClient(Qapp, tmpdir=None)[source]

Setup for marking client and annotator

Notes

TODO: should be a QMainWindow but at any rate not a Dialog TODO: should this be parented by the QApplication?

Initialize a new MarkerClient

Parameters:
  • Qapp (QApplication) – Main client application

  • tmpdir (pathlib.Path/str/None) – a temporary directory for storing image files and other data. In principle can be shared with Identifier although this may not be implemented. If None, we will make our own.

UIInitialization()[source]

Startup procedure for the user interface

Returns:

Modifies self.ui

Return type:

None

annotateTest()[source]

Grab current test from table, do checks, start annotator.

applyLastTimeOptions(lastTime)[source]

Applies all settings from previous client.

Parameters:
  • lastTime (dict) – information about settings, often from a

  • run. (config file such as from the last time the client was) –

Returns:

None

backgroundUploadFailed(task, errmsg)[source]

An upload has failed, we don’t know why, do something LOUDLY.

Parameters:
  • task (str) – the task ID of the current test.

  • errmsg (str) – the error message.

Returns:

None

backgroundUploadFailedServerChanged(task, error_message)[source]

An upload has failed because server changed something, safest to quit.

Parameters:
  • task (str) – the task ID of the current test.

  • error_message (str) – a brief description of the error.

Returns:

None

backgroundUploadFinished(task, numDone, numtotal)[source]

An upload has finished, do appropriate UI updates

Parameters:
  • task (str) – the task ID of the current test.

  • numDone (int) – number of exams marked

  • numTotal (int) – total number of exams to mark.

Returns:

None

cacheLatexComments()[source]

Caches Latexed comments.

callbackAnnDoneCancel(task)[source]

Called when anotator is done grading.

Parameters:

task (str) – task name

Returns:

None

callbackAnnDoneClosing(task)[source]

Called when annotator is done grading and is closing.

Parameters:

task (str) – the task ID of the current test.

Returns:

None

callbackAnnWantsUsToUpload(task, stuff)[source]

Called when annotator wants to upload.

Parameters:
  • task (str) – the task ID of the current test.

  • stuff (list) – a list containing grade(int): grade given by marker. markingTime(int): total time spent marking. paperDir(dir): Working directory for the current task aname(str): annotated file name plomFileName(str): the name of the .plom file rubric(list[str]): the keys of the rubrics used integrity_check(str): the integrity_check string of the task.

Returns:

None

claim_task_and_trigger_downloads(task)[source]

Claim a particular task for the current user and start image downloads.

Notes

Side effects: on success, updates the table of tasks by adding a new row. The new row is not automatically selected.

Returns:

None

Raises:
  • PlomTakenException

  • PlomVersionMismatchException

closeEvent(self, QCloseEvent)[source]
connectGuiButtons()[source]

Connect gui buttons to appropriate functions

Notes

TODO: remove the total-radiobutton

Returns:

None - Modifies self.ui

deferTest()[source]

Mark test as “defer” - to be skipped until later.

ensureAllDownloaded(new, old)[source]

Whenever the selection changes, ensure downloaders are either finished or running for each image.

We might need to restart downloaders if they have repeatedly failed. Even if we are still waiting, we can signal to the download the we have renewed interest in this particular download. TODO: for example. maybe we should send a higher priority? No: currently this also happens “in the background” b/c Marker selects the new row.

Parameters:
  • new (QItemSelection) – the newly selected cells.

  • old (QItemSelection) – the previously selected cells.

Returns:

None

getDataForAnnotator(task)[source]

Start annotator on a particular task.

Parameters:

task (str) – the task id. If original qXXXXgYY, then annotated version is GXXXXgYY (G=graded).

Returns:

as described by startTheAnnotator, if successful.

Return type:

list/None

getMorePapers(oldtgvID)[source]

Loads more tests.

Parameters:

oldtgvID (str) – the Test-Group-Version ID for the previous test.

Returns:

as described by getDataForAnnotator

Return type:

initialData

getRubricsFromServer(question=None)[source]

Get list of rubrics from server.

Parameters:

question (int/None) –

Returns:

A list of the dictionary objects.

Return type:

list

getTabStateFromServer()[source]

Download the state from the server.

get_downloads_for_src_img_data(src_img_data, trigger=True)[source]

Make sure the images for some source image data are downloaded.

If an image is not yet downloaded, trigger the download again.

Parameters:

src_img_data (list) – list of dicts. Note we may modify this so pass a copy if you don’t want this! Specifically, the "filename" key is inserted or replaced with the path to the downloaded image or the placeholder image.

Keyword Arguments:

trigger (bool) – if True we trigger background jobs for any that have not been downloaded.

Returns:

True if all images have already been downloaded, False if at least one was not. In the False case, downloads have been triggered; wait; process events; then call back if you want.

Return type:

bool

get_file_for_previous_viewer(task)[source]

Get the annotation file for the given task. Check to see if the local system already has the files for that task and if not grab them from the server. Then pass the annotation-image-file back to the caller.

get_files_for_previously_annotated(task)[source]

Loads the annotated image, the plom file, and the original source images.

Parameters:

task (str) – the task for the image files to be loaded from. Takes the form “q1234g9” = test 1234 question 9

Returns:

currently this returns True. Unless it fails which will induce a crash (after some popup dialogs).

Return type:

bool

Raises:

Uses error dialogs; not currently expected to throw exceptions

get_upload_queue_length()[source]

How long is the upload queue?

An overly long queue might be a sign of network troubles.

Returns:

The number of papers waiting to upload, possibly but not certainly including the current upload-in-progress. Value might also be approximate.

Return type:

int

latexAFragment(txt, *, quiet=False, cache_invalid=True, cache_invalid_tryagain=False)[source]

Run LaTeX on a fragment of text and return the file name of a png.

The files are cached for reuse if the same text is passed again.

Parameters:

txt (str) – the text to be Latexed.

Keyword Arguments:
  • quiet (bool) – if True, don’t popup dialogs on errors. Caution: this can result in a lot of API calls because users can keep requesting the same (bad) TeX from the server, e.g., by having bad TeX in a rubric.

  • cache_invalid (bool) – whether to cache invalid TeX. Useful to prevent repeated calls to render bad TeX but might prevent users from seeing (again) an error dialog that

  • try_again_if_cache_invalid (bool) – if True then when we get a cache hit of None (corresponding to bad TeX) then we try to to render again.

Returns:

a path and filename to a .png of the rendered TeX. Or None if there was an error: callers will need to decide how to handle that, typically by displaying the raw code instead.

Return type:

pathlib.Path/str/None

loadMarkedList()[source]

Loads the list of previously marked papers into self.examModel

Returns:

None

manage_tags()[source]

Manage the tags of the current task.

manage_task_tags(task, parent=None)[source]

Manage the tags of a task.

Parameters:

task (str) – A string like “q0003g2” for paper 3 question 2.

Keyword Arguments:

parent (Window/None) – Which window should be dialog’s parent? If None, then use self (which is Marker) but if other windows (such as Annotator or PageRearranger) are calling this and if so they should pass themselves: that way they would be the visual parents of this dialog.

moveSelectionToTask(task)[source]

Update the selection in the list of papers.

moveToNextUnmarkedTest(task=None)[source]

Move the list to the next unmarked test, if possible.

Parameters:

task (str) – the task number of the next unmarked test.

Returns:

True if move was successful, False if not, for any reason.

property prefer_above

User prefers to mark papers above this value, or None if no preference.

requestInteractive()[source]

Ask user for paper number and then ask server for that paper.

If available, download stuff, add to list, update view.

requestNext(*, update_select=True)[source]

Ask server for an unmarked paper, get file, add to list, update view.

Retry a few times in case two clients are asking for same.

Keyword Arguments:

update_select (bool) – default True, send False if you don’t want to adjust the visual selection.

Returns:

None

requestNextInBackgroundStart()[source]

Requests the next TGV in the background.

Returns:

None

saveTabStateToServer(tab_state)[source]

Upload a tab state to the server.

setFilter()[source]

Sets a filter tag.

setup(messenger, question, version, lastTime)[source]

Performs setup procedure for markerClient.

TODO: move all this into init?

TODO: verify all lastTime Params, there are almost certainly some missing

Parameters:
  • messenger (Messenger) – handle communication with server.

  • question (int) – question number.

  • version (int) – version number

  • lastTime (dict) –

    settings. containing:

    {
      "POWERUSER"
      "FOREGROUND"
      "CommentsWarnings"
      "MarkWarnings"
      "KeyBinding"
    }
    

    and potentially others

Returns:

None

startTheAnnotator(initialData)[source]

This fires up the annotation window for user annotation + marking.

Parameters:

initialData (list) – containing things documented elsewhere in plom.client.annotator.Annotator.__init__().

Returns:

None

updatePreviewImage(new, old)[source]

Updates the displayed image when the selection changes.

Parameters:
  • new (QItemSelection) – the newly selected cells.

  • old (QItemSelection) – the previously selected cells.

Returns:

None

updateProgress(val=None, maxm=None)[source]

Updates the progress bar.

Parameters:
  • val (int) – value for the progress bar

  • maxm (int) – maximum for the progress bar.

Returns:

None

view_testnum_question()[source]

Shows a particular paper number and question.

wait_for_bguploader(timeout=0)[source]

Wait for the uploader queue to empty.

Parameters:

timeout (int) – return early after approximately timeout seconds. If 0 then wait forever.

Returns:

True if it shutdown. False if we timed out.

Return type:

bool

class plom.client.annotator.Annotator(username, parentMarkerUI=None, initialData=None)[source]

The main annotation window for annotating group-images.

A subclass of QWidget

Initializes a new annotator window.

Parameters:
  • username (str) – username of Marker

  • parentMarkerUI (MarkerClient) – the parent of annotator UI.

  • initialData (dict) – as documented by the arguments to “load_new_question”

addImageMode()[source]

Opens a file dialog for images, shows a message box if the image is too large, otherwise continues to image mode.

Notes

If the Image is greater than 200kb, will return an error.

Returns:

None

changeCBZoom(CBIndex)[source]

Keeps zoom combo box at selected index.

Parameters:

CBIndex (int) – the current zoom Combo Box Index

Returns:

Modifies self.ui

Return type:

None

change_annot_scale(scale=None)[source]

Change the scale of the annotations.

Parameters:

scale (float/None) – if None reset the scale to the default. If any floating point number, multiple the scale by that value.

change_annotation_colour()[source]

Ask user for a new colour for the annotations.

closeEvent(event)[source]

Overrides QWidget.closeEvent().

Deal with various cases of window trying to close.

Notes: These include:

  • User closes window via titlebar close icon (or alt-f4 or…)

  • User clicks “Cancel”

  • User clicks “Done”

Window close or Cancel are currently treated the same way: discard all annotations.

Parameters:

event – the event of the window closing.

Returns:

modifies many instance vars.

Return type:

None

close_current_question()[source]

Closes the current question, closes scene and clears instance vars.

Notes

As a result of this method, many instance variables will be None. Be cautious of how these variables will be handled in cases where they are None.

Returns:

Modifies self.

Return type:

None

close_current_scene()[source]

Removes the current cene, saving some info in case we want to open a new one.

Returns:

Modifies self.

Return type:

None

createNewRubric(new_rubric)[source]

Ask server to create a new rubric with data supplied

getRubricsFromServer()[source]

Request a latest rubric list for current question.

getTabStateFromServer()[source]

Have Marker download the tab state from the server.

get_nonrubric_text_from_page()[source]

Retrieves text (not in rubrics) from the scene.

Returns:

strings for text annotations not in a rubric.

Return type:

list

handleRubric(rubric)[source]

Pass a rubric dict onward to the scene, if we have a scene.

Parameters:

rubric (dict) – we don’t care what’s in it: that’s for the scene and the rubric widget to agree on!

Returns:

Modifies self.scene

Return type:

None

isZoomFitHeight()[source]

Sets the zoom ui text when user has selected “Fit Height.”

Returns:

Modifies self.ui

Return type:

None

isZoomFitWidth()[source]

Sets the zoom ui text when user has selected “Fit Width.”

Returns:

Modifies self.ui

Return type:

None

is_dirty()[source]

Is the scene dirty?

Has the scene been annotated or changed this session? Re-opening a previous annotated scene does not dirty it, until changes are made. Changes could be made and then undone back to the clean state. The concept should be familiar to “file saved” in a text editor.

keyPopUp(*, tab_idx=None)[source]

View help and keyboard shortcuts, eventually edit them.

Keyword Arg:
tab_idx (int/None): which tab to open in the help. If None

then we try to re-open on the same tab from last run.

keyToChangeRubric(keyNumber)[source]

Translates a the numerical key into a selection of that visible row of the current rubric tab.

Returns:

modifies self.rubric_widget

Return type:

None

latexAFragment(*args, **kwargs)[source]

Latex a fragment of text.

loadCursors()[source]

Load custom cursors and set their hotspots.

Returns:

None

loadWindowSettings()[source]

Loads the window settings.

load_new_question(tgvID, question_label, version, max_version, testName, paperdir, saveName, maxMark, plomDict, integrity_check, src_img_data)[source]

Loads new data into the window for marking.

Parameters:
  • tgvID (str) – Test-Group-Version ID code. For example, for Test #0027, group #13, version #2, we have t0027g13v2. TODO: currently only t0027g13, no version, despite name.

  • question_label (str) – The name of the question we are marking. This is generally used for display only as there is an integer for precise usage.

  • version (int) – which version are we working on?

  • max_version (int) – what is the largest version in this assessment?

  • testName (str) – Test Name

  • paperdir (dir) – Working directory for the current task

  • saveName (str/pathlib.Path) – file name (and dir, optionally) of the basename to save things (no .png/.jpg extension) If it does have an extension, it will be ignored.

  • maxMark (int) – maximum possible score for that test question

  • plomDict (dict) – a dictionary of annotation information. Contains sufficient information to recreate the annotation objects on the page if you go back to continue annotating a question.

  • integrity_check (str) – integrity check string

  • src_img_data (list[dict]) – image md5sums, filenames etc.

Returns:

Modifies many instance vars.

Return type:

None

modifyRubric(key, updated_rubric)[source]

Ask server to create a new rubric with data supplied

narrowLayout()[source]

Changes view to narrow Layout style.

Returns:

modifies self.ui

Return type:

None

new_or_permuted_image_data(src_img_data)[source]

We have permuted/added/removed underlying source images, tear done and build up again.

next_minor_tool(dir=1, always_move=False)[source]

Switch to current minor tool or advance to next minor tool.

Parameters:
  • dir (int) – +1 for next (default), -1 for previous.

  • always_move (bool) – the minor tools keep track of the last-used tool. Often, but not always, we want to switch back to the last-used tool. False by default.

pickleIt()[source]

Capture the annotated pages as a bitmap and a .plom file.

  1. Renders the current scene as a static bitmap.

  2. Retrieves current annotations in reverse chronological order.

  3. Adds various other metadata.

  4. Writes JSON into the .plom file.

Note: called “pickle” for historical reasons: it is neither a Python pickle nor a real-life pickle.

Returns:

two pathlib.Path, one for the rendered image and one for the .plom file.

Return type:

tuple

prev_minor_tool()[source]

Switch backward to the previous minor tool.

rearrangePages()[source]

Rearranges pages in UI.

Returns:

None

redo()[source]

Redoes the last action in the UI.

refreshDisplayedMark(score)[source]

Update the marklabel (and narrow one) with the current score - triggered by pagescene

Returns:

None

refreshRubrics()[source]

ask the rubric widget to refresh rubrics

rubricMode()[source]

Changes the tool to rubric.

saveAndClose()[source]

Save the current annotations, and then close.

Returns:

alters self.scene

Return type:

None

saveAndGetNext()[source]

Saves the current annotations, and moves on to the next paper.

saveAnnotations()[source]

Try to save the annotations and signal Marker to upload them.

Notes

There are various sanity checks and user interaction to be done. Return False if user cancels. Return True if we should move on (for example, to close the Annotator).

Be careful of a score of 0 - when mark total or mark up. Be careful of max-score when marking down. In either case - get user to confirm the score before closing. Also confirm various “not enough feedback” cases.

Returns:

False if user cancels, True if annotator is closed successfully.

saveTabStateToServer(tab_state)[source]

Have Marker upload this tab state to the server.

saveWindowSettings()[source]

Saves current window settings and other state into the parent.

Returns:

modifies self.parentMarkerUI and self.scene

Return type:

None

setAllIcons()[source]

Sets all icons for the ui Tool Buttons.

Returns:

Modifies ui Tool Buttons.

Return type:

None

setButtons()[source]

Connects buttons to their corresponding functions.

setIcon(toolButton, name, iconfile)[source]

Sets a name and svg icon for a given QToolButton.

Parameters:
  • toolButton (QToolButton) – the ui Tool Button for a name and icon to be added to.

  • name (str) – a name defining toolButton.

  • iconfile (str) – filename of .svg, must be in the resource plom.client.icons.

Returns:

alters toolButton

Return type:

None

setMinorShortCuts()[source]

Setup non-editable shortcuts.

Each of these actions can be associated with multiple shortcut keys.

setToolMode(newMode, *, cursor=None, imagePath=None)[source]

Changes the current tool mode and cursor.

Parameters:

newMode (str) – "move", "rubric" etc.

Keyword Arguments:
  • imagePath – an argument for the “image” tool, used used only by the image tool.

  • cursor (str) – if None or omitted default cursors are used for each tool. If needed you could override this. (currently unused, semi-deprecated).

Notes

TODO: this does various other mucking around for legacy reasons: could probably still use some refactoring.

Returns:

Modifies self

Return type:

None

setToolShortCuts()[source]

Set or change the shortcuts for the basic tool keys.

These are the shortcuts that are user-editable.

setViewAndScene(src_img_data)[source]

Makes a new scene (pagescene object) and connects it to the view (pageview object).

The pageview (which is a qgraphicsview) which is (mostly) a layer between the annotation widget and the graphics scene which actually stores all the graphics objects (the image, lines, boxes, text etc etc). The view allows us to zoom pan etc over image and its annotations.

Returns:

modifies self.scene from None to a pagescene object and connects it to a pageview object.

Return type:

None

setZoomComboBox()[source]

Sets the combo box for the zoom method.

Returns:

Modifies self.ui

Return type:

None

swapMaxNorm()[source]

Toggles the window size between max and normal.

Returns

None: modifies self.windowState

toggleTools()[source]

Shows/Hides tools making more space to view the group-image.

Returns:

modifies self.ui.hideableBox

Return type:

None

undo()[source]

Undoes the last action in the UI.

unpickleIt(plomData)[source]

Unpickles the page by calling scene.unpickleSceneItems and sets the page’s mark.

Parameters:

plomData (dict) – a dictionary containing the data for the pickled .plom file.

Returns:

None

update_annot_scale_menu_label()[source]

Update the menu which shows the current annotation scale.

viewWholePaper()[source]

Popup a dialog showing the entire paper.

TODO: this has significant duplication with RearrangePages.

Returns:

None

wideLayout()[source]

Changes view to Wide Layout style.

Returns:

modifies self.ui

Return type:

None

zoomCBChanged()[source]

Modifies the page view based on the selected zoom option.

Returns:

Modifies self.ui

Return type:

None

The background downloader downloads images using threads.

class plom.client.downloader.DownloadWorker(msgr, img_id, md5, target_name, *, basedir, simulate_failures=False)[source]
run(self)[source]
class plom.client.downloader.Downloader(basedir, *, msgr=None)[source]

Downloads and maintains a cache of images.

Downloader maintains a queue of downloads and emits signals whenever downloads succeed or fail.

Call download_in_background_thread() to enqueue an image for asynchronous download. Once enqueued, a download will be automatically retried several times, but to prevent endless data usage, it will give up after three tries. That is, clients cannot assume that something enqueued will inevitably be downloaded. Clients can check by TODO: document how to check if something is in the queue or/and or currently downloading.

Synchronous downloads can be performed with sync_download() and sync_downloads(). These images will also be cached.

TODO: document how to query the queue size. TODO: document how to query the size on disc.

The current queue can be cleared with clear_queue(). For shutting down the queue, see stop(). The Downlaoder keeps a clone of the messenger: if you logout (revoke the token) in another msgr while this is downloading, you’ll get a crash.

The Downloader will emit various signals. You can connect slots to these:

  • download_finished(img_id: int, md5sum: str, filename: str): emitted when a (background) download finishes. filename is newly-downloaded file.

  • download_failed(img_id: int): emitted when a (background) download fails. The job will be automatically restarted up to three times.

  • download_queue_changed(dict): the queue length changed (e.g., something enqueued or the queue is cleared). The signal argument is a dict of information about the queue.

Use enable_fail_mode() to artificially fail some download attempts and generally take longer. For debugging. Disable again with disable_fail_mode().

Initialize a new Downloader.

Parameters:

basedir (pathlib.Path/str) – a directory for the image cache.

Keyword Arguments:

msgr (Messenger) – Note Messenger is not multithreaded and blocks using mutexes. Here we make our own private clone so caller can keep using their’s.

attach_messenger(msgr)[source]

Add/replace the current messenger.

clear_queue()[source]

Cancel any enqueued (but not yet started) downloads.

Any existing downloads will continue, including their (up-to) three retries.

detach_messenger()[source]

Stop our messenger and forget it (but do not logout).

download_in_background_thread(row, priority=False, _is_retry=False)[source]

Enqueue the downloading of particular row of the image database.

Parameters:

row (dict) – One image entry in the “page data”, has fields id, md5 and some others that are used to try to choose a reasonable local file name. Currently the local file name is chosen from the "server_path" key.

Keyword Arguments:
  • priority (bool) – high priority if user requested this (not a background download.

  • _is_retry (bool) – default False. If True, this signifies an automatic retry. Clients should probably not touch this.

Does not start a new download if the Page Cache already has that image. It also tries to avoid enquing another request for the same image.

get_placeholder_path()[source]

A static image that can be used as a placeholder while images are downloading.

Returns:

a real path on disc to the image, possibly a cached

copy. But for now its a str (TODO?).

Return type:

pathlib.Path

Currently you may have to make a string (not an Path for example) b/c of some Qt limitations in the ExamModel and proxy stuff in Marker.

TODO: Issue #2357: better image or perhaps an animation?

has_messenger()[source]

Do we have a messenger?

stop(timeout=- 1)[source]

Try to stop the downloader, after waiting for threads to clear.

Parameters:

timeout (int) – milliseconds seconds to wait before giving up. -1 to wait forever.

Returns:

True if all threads finished or False if timeout reached.

Return type:

bool

sync_download(row)[source]

Given a row of “pagedata”, download synchronously and return edited row.

Parameters:

row (dict) – one row of the metadata for the set of all pages involved in a question. A list of dicts where each dict must have (at least) keys id, md5, server_path. TODO: sometimes we seem to accept md5sum instead: should fix that.

Returns:

the modified row. If the file was already downloaded, put its name into the filename key. If we had to download it we also put the filename into filename.

Return type:

dict

sync_downloads(pagedata)[source]

Given a block of “pagedata” download all images synchronously and return updated data.

Parameters:

pagedata (list) – a list of dicts, each dict described in sync_download. Warning: we don’t make a copy: it will be modified (and returned).

Returns:

a list of dicts which consists of the updated input with filenames added/updated for each image.

Return type:

list

class plom.client.downloader.WorkerSignals[source]

Defines the signals available from a running worker thread.

Supported signals are:

finished:

No data

download_success:

(img_id (int), md5 (str), tempfile (str), targetfile (str)

download_fail:

(img_id (int), md5 (str), targetfile (str), err_stuff_tuple (tuple) where the tuple is (exctype, value, traceback.format_exc().

plom.manager module

Plom server management tools.

class plom.manager.Manager(Qapp, *, server=None, user=None, password=None, manager_msgr=None)[source]

Plom server management and marking progress UI tool.

Start a new Plom Manager window.

Parameters:

Qapp (QApplication) –

Keyword Arguments:
  • manager_msgr (ManagerMessenger/None) – a connected ManagerMessenger. Note that the plain ‘ol Messenger will not work. By default or if None is passed, we’ll make the user login or use other kwargs.

  • server (str/None) –

  • user (str/None) –

  • password (str/None) –

closeEvent(self, QCloseEvent)[source]
manage_task_tags(paper_num, question, parent=None)[source]

Manage the tags of a task.

Parameters:
  • paper_num (int/str) –

  • question (int/str) –

Keyword Arguments:

parent (Window/None) – Which window should be dialog’s parent? If None, then use self (which is Marker) but if other windows (such as Annotator or PageRearranger) are calling this and if so they should pass themselves: that way they would be the visual parents of this dialog.

Returns:

the current tags of paper/question. Note even if the dialog is cancelled, this will be updated (as someone else could’ve changed tags).

Return type:

list

refreshMRev()[source]

Refresh user list, tags, and any other server-dependent fields in marking review tab.

setFont(self, QFont)[source]