def IDrequestPredictions(self): self.SRmutex.acquire() try: response = self.session.get( "https://{}/ID/predictions".format(self.server), json={ "user": self.user, "token": self.token }, verify=False, ) response.raise_for_status() # TODO: print(response.encoding) autodetected predictions = StringIO(response.text) except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 404: raise PlomSeriousException( "Server cannot find the prediction list.") from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return predictions
def MsetTag(self, code, tags): self.SRmutex.acquire() try: response = self.session.patch( "https://{}/MK/tags/{}".format(self.server, code), json={ "user": self.user, "token": self.token, "tags": tags }, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 409: raise PlomTakenException( "Task taken by another user.") from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release()
def MgetRubricsByQuestion(self, question_number): """Retrieve list of all rubrics from server for given question. Args: question_number (int) Raises: PlomAuthenticationException: Authentication error. PlomSeriousException: Other error types, possible needs fix or debugging. Returns: list: list of dicts, possibly an empty list if server has no rubrics for this question. """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/rubric/{}".format(self.server, question_number), json={ "user": self.user, "token": self.token, }, verify=False, ) response.raise_for_status() return response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Error of type {} getting rubric list".format(e)) from None finally: self.SRmutex.release()
def IDaskNextTask(self): """Return the TGV of a paper that needs IDing. Return: string or None if no papers need IDing. Raises: SeriousError: if something has unexpectedly gone wrong. """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/ID/tasks/available".format(self.server), json={ "user": self.user, "token": self.token }, verify=False, ) # throw errors when response code != 200. response.raise_for_status() if response.status_code == 204: return None tgv = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return tgv
def MaskNextTask(self, q, v): """Ask server for a new marking task, return tgv or None. None indicated no more tasks available. TODO: why are we using json for a string return? """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/tasks/available".format(self.server), json={ "user": self.user, "token": self.token, "q": q, "v": v }, verify=False, ) # throw errors when response code != 200. if response.status_code == 204: return None response.raise_for_status() # convert the content of the response to a textfile for identifier tgv = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return tgv
def IDrequestImage(self, code): self.SRmutex.acquire() try: response = self.session.get( "https://{}/ID/images/{}".format(self.server, code), json={ "user": self.user, "token": self.token }, verify=False, ) response.raise_for_status() imageList = [] for img in MultipartDecoder.from_response(response).parts: imageList.append(BytesIO( img.content).getvalue()) # pass back image as bytes except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 404: raise PlomSeriousException( "Cannot find image file for {}.".format(code)) from None elif response.status_code == 409: raise PlomSeriousException( "Another user has the image for {}. This should not happen" .format(code)) from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return imageList
def MrequestWholePaperMetadata(self, code, questionNumber=0): """Get metadata about the images in this paper. TODO: questionnumber? why? TODO: returns 404, so why not raise that instead? """ self.SRmutex.acquire() # note - added default value for questionNumber so that this works correctly # when called from identifier. - Fixes #921 try: response = self.session.get( "https://{}/MK/TMP/whole/{}/{}".format(self.server, code, questionNumber), json={ "user": self.user, "token": self.token }, verify=False, ) response.raise_for_status() ret = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return ret
def MrequestDoneTasks(self, q, v): self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/tasks/complete".format(self.server), json={ "user": self.user, "token": self.token, "q": q, "v": v }, verify=False, ) response.raise_for_status() mList = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return mList
def IDclaimThisTask(self, code): self.SRmutex.acquire() try: response = self.session.patch( "https://{}/ID/tasks/{}".format(self.server, code), json={ "user": self.user, "token": self.token }, verify=False, ) if response.status_code == 204: raise PlomTakenException("Task taken by another user.") response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() imageList = [] for img in MultipartDecoder.from_response(response).parts: imageList.append(BytesIO( img.content).getvalue()) # pass back image as bytes return imageList
def MlatexFragment(self, latex): self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/latex".format(self.server), json={ "user": self.user, "token": self.token, "fragment": latex }, verify=False, ) response.raise_for_status() image = BytesIO(response.content).getvalue() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 406: raise PlomLatexException( "There is an error in your latex fragment") from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return image
def IDprogressCount(self): self.SRmutex.acquire() try: response = self.session.get( "https://{}/ID/progress".format(self.server), json={ "user": self.user, "token": self.token }, verify=False, ) # throw errors when response code != 200. response.raise_for_status() # convert the content of the response to a textfile for identifier progress = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return progress
def MgetRubrics(self): """Retrieve list of all rubrics from server. Raises: PlomAuthenticationException: Authentication error. PlomSeriousException: any other unexpected error. Returns: list: list of dicts, possibly an empty list if server has no rubrics. """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/rubric".format(self.server), json={ "user": self.user, "token": self.token, }, verify=False, ) response.raise_for_status() return response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Error of type {} getting rubric list".format(e)) from None finally: self.SRmutex.release()
def MclaimThisTask(self, code): """Claim a task from server and get back metadata. args: code (str): a task code such as `"q0123g2"`. returns: list: Consisting of image_metadata, tags, integrity_check. """ self.SRmutex.acquire() try: response = self.session.patch( "https://{}/MK/tasks/{}".format(self.server, code), json={ "user": self.user, "token": self.token }, verify=False, ) response.raise_for_status() if response.status_code == 204: raise PlomTakenException("Task taken by another user.") ret = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return ret
def createNewBundle(self, bundle_name, md5sum): """Ask server to create bundle with given name/md5sum. Server will check name / md5sum of bundle. * If bundle matches either 'name' or 'md5sum' then return [False, reason] - this shouldnt 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. """ self.SRmutex.acquire() try: response = self.session.put( "https://{}/admin/bundle".format(self.server), json={ "user": self.user, "token": self.token, "bundle": bundle_name, "md5sum": md5sum, }, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return response.json()
def doesBundleExist(self, bundle_name, md5sum): """Ask server if given bundle exists. Checks bundle's md5sum and name: * neither = no matching bundle, return [False] * 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. """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/admin/bundle".format(self.server), json={ "user": self.user, "token": self.token, "bundle": bundle_name, "md5sum": md5sum, }, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return response.json()
def MrequestOriginalImages(self, task): self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/originalImages/{}".format(self.server, task), json={ "user": self.user, "token": self.token }, verify=False, ) if response.status_code == 204: raise PlomNoMoreException("No task = {}.".format(task)) response.raise_for_status() # response is [image1, image2,... image.n] imageList = [] for img in MultipartDecoder.from_response(response).parts: imageList.append(BytesIO(img.content).getvalue()) except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 404: raise PlomNoMoreException( "Cannot find image file for {}".format(task)) from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return imageList
def sidToTest(self, student_id): """Ask server to match given student_id to a test-number. Returns * [True, test_number] * [False, 'Cannot find test with that student id'] """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/admin/sidToTest".format(self.server), json={ "user": self.user, "token": self.token, "sid": student_id, }, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return response.json()
def MmodifyRubric(self, key, new_rubric): """Ask server to modify a rubric and get key back. Args: rubric (dict): the modified rubric info as dict. Raises: PlomAuthenticationException: Authentication error. PlomSeriousException: Other error types, possible needs fix or debugging. Returns: list: A list of: [False] If operation was unsuccessful. [True, updated_commments_list] including the new comments. """ self.SRmutex.acquire() try: response = self.session.patch( "https://{}/MK/rubric/{}".format(self.server, key), json={ "user": self.user, "token": self.token, "rubric": new_rubric, }, verify=False, ) response.raise_for_status() new_key = response.json() messenger_response = [True, new_key] except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 400: raise PlomSeriousException( "Key mismatch in request.") from None elif response.status_code == 406: raise PlomSeriousException( "Rubric sent was incomplete.") from None elif response.status_code == 409: raise PlomSeriousException( "No rubric with that key found.") from None else: raise PlomSeriousException( "Error of type {} when creating new rubric".format( e)) from None messenger_response = [False] finally: self.SRmutex.release() return messenger_response
def MgetUserRubricTabs(self, question): """Ask server for the user's rubric-tabs config file for this question Args: question (int): which question to save to (rubric tabs are generally per-question). Raises: PlomAuthenticationException PlomSeriousException Returns: tuple: First element is bool for success. If True, second element is a dict of information about user's tabs. """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/user/{}/{}".format(self.server, self.user, question), json={ "user": self.user, "token": self.token, "question": question, }, verify=False, ) response.raise_for_status() if response.status_code == 200: paneConfig = response.json() return [True, paneConfig] elif response.status_code == 204: return [False] # server has no data else: raise PlomSeriousException( "No other 20x response should come from server.") from None except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 403: raise PlomSeriousException(response.text) from None else: raise PlomSeriousException( "Error of type {} when creating new rubric".format( e)) from None finally: self.SRmutex.release()
def requestAndSaveToken(self, user, pw): """Get a authorisation token from the server. The token is then used to authenticate future transactions with the server. raises: PlomAPIException: a mismatch between server/client versions. PlomExistingLoginException: user already has a token: currently, we do not support getting another one. PlomAuthenticationException: wrong password, account disabled, etc: check contents for details. PlomSeriousException: something else unexpected such as a network failure. """ self.SRmutex.acquire() try: response = self.session.put( "https://{}/users/{}".format(self.server, user), json={ "user": user, "pw": pw, "api": Plom_API_Version, "client_ver": __version__, }, verify=False, timeout=5, ) # throw errors when response code != 200. response.raise_for_status() self.token = response.json() self.user = user except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException(response.json()) from None elif response.status_code == 400: raise PlomAPIException(response.json()) from None elif response.status_code == 409: raise PlomExistingLoginException(response.json()) from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None except requests.ConnectionError as err: raise PlomSeriousException( "Cannot connect to server\n {}\n Please check details before trying again." .format(self.server)) from None finally: self.SRmutex.release()
def MrequestOneImage(self, task_code, image_id, md5sum): """Download one image from server by its database id. args: code (str): the task code such as "q1234g9". TODO: consider removing code/`task` from URL. image_id (int): TODO: int/str? The key into the server's database of images. md5sum (str): the expected md5sum, just for sanity checks or something I suppose. return: bytes: png/jpeg or whatever as bytes. Errors/Exceptions 401: not authenticated 404: no such image 409: wrong md5sum provided """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/images/{}/{}/{}".format(self.server, task_code, image_id, md5sum), json={ "user": self.user, "token": self.token }, verify=False, ) response.raise_for_status() image = BytesIO(response.content).getvalue() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 409: raise PlomConflict("Wrong md5sum provided") from None elif response.status_code == 404: raise PlomNoMoreException("Cannot find image") from None else: raise PlomSeriousException( "Some other unexpected error {}".format(e)) from None finally: self.SRmutex.release() return image
def IDrequestClasslist(self): """Ask server for the classlist. Returns: list: ordered list of (student id, student name) pairs. Both are strings. Raises: PlomAuthenticationException: login troubles. PlomBenignException: server has no classlist. PlomSeriousException: all other failures. """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/ID/classlist".format(self.server), json={ "user": self.user, "token": self.token }, verify=False, ) # throw errors when response code != 200. response.raise_for_status() # you can assign to the encoding to override the autodetection # TODO: define API such that classlist must be utf-8? # print(response.encoding) # response.encoding = 'utf-8' # classlist = StringIO(response.text) classlist = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 404: raise PlomBenignException( "Server cannot find the class list") from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return classlist
def uploadHWPage(self, sid, question, order, sname, fname, md5sum, bundle, bundle_order): self.SRmutex.acquire() try: param = { "user": self.user, "token": self.token, "fileName": sname, "sid": sid, "question": question, "order": order, "md5sum": md5sum, "bundle": bundle, "bundle_order": bundle_order, } mime_type = mimetypes.guess_type(sname)[0] dat = MultipartEncoder( fields={ "param": json.dumps(param), "originalImage": (sname, open(fname, "rb"), mime_type), # image }) response = self.session.put( "https://{}/admin/hwPages".format(self.server), json={ "user": self.user, "token": self.token }, data=dat, headers={"Content-Type": dat.content_type}, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return response.json()
def MrequestWholePaper(self, code, questionNumber=0): self.SRmutex.acquire() # note - added default value for questionNumber so that this works correctly # when called from identifier. - Fixes #921 try: response = self.session.get( "https://{}/MK/whole/{}/{}".format(self.server, code, questionNumber), json={ "user": self.user, "token": self.token }, verify=False, ) response.raise_for_status() # response should be multipart = [ pageData, f1,f2,f3..] imagesAsBytes = MultipartDecoder.from_response(response).parts images = [] i = 0 for iab in imagesAsBytes: if i == 0: pageData = json.loads(iab.content) else: images.append(BytesIO( iab.content).getvalue()) # pass back image as bytes i += 1 except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None # TODO? elif response.status_code == 409: raise PlomTakenException( "Task taken by another user.") from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return [pageData, images]
def MsaveUserRubricTabs(self, question, tab_config): """Cache the user's rubric-tabs config for this question onto the server Args: question (int): the current question number tab_config (dict): the user's rubric pane configuration for this question. Raises: PlomAuthenticationException PlomSeriousException Returns: None """ self.SRmutex.acquire() try: response = self.session.put( "https://{}/MK/user/{}/{}".format(self.server, self.user, question), json={ "user": self.user, "token": self.token, "question": question, "rubric_config": tab_config, }, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 403: raise PlomSeriousException(response.text) from None else: raise PlomSeriousException( "Error of type {} when creating new rubric".format( e)) from None finally: self.SRmutex.release()
def clearAuthorisation(self, user, pw): self.SRmutex.acquire() try: response = self.session.delete( "https://{}/authorisation".format(self.server), json={ "user": user, "password": pw }, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release()
def getMissingHW(self): self.SRmutex.acquire() try: response = self.session.get( "https://{}/REP/missingHW".format(self.server), verify=False, json={ "user": self.user, "token": self.token }, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return response.json()
def triggerUpdateAfterLUpload(self): self.SRmutex.acquire() try: response = self.session.put( "https://{}/admin/loosePagesUploaded".format(self.server), verify=False, json={ "user": self.user, "token": self.token }, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return response.json()
def MdidNotFinishTask(self, code): self.SRmutex.acquire() try: response = self.session.delete( "https://{}/MK/tasks/{}".format(self.server, code), json={ "user": self.user, "token": self.token }, verify=False, ) response.raise_for_status() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return True
def MgetMaxMark(self, question, ver): """Get the maximum mark for this question and version. Raises: PlomRangeExeception: `question` or `ver` is out of range. PlomAuthenticationException: PlomSeriousException: something unexpected happened. """ self.SRmutex.acquire() try: response = self.session.get( "https://{}/MK/maxMark".format(self.server), json={ "user": self.user, "token": self.token, "q": question, "v": ver }, verify=False, ) # throw errors when response code != 200. response.raise_for_status() # convert the content of the response to a textfile for identifier maxMark = response.json() except requests.HTTPError as e: if response.status_code == 401: raise PlomAuthenticationException() from None elif response.status_code == 416: raise PlomRangeException(response.text) from None else: raise PlomSeriousException( "Some other sort of error {}".format(e)) from None finally: self.SRmutex.release() return maxMark