def get_max_seconds(default=120): """Returns the max number of seconds to wait for a task to finish """ registry_id = "senaite.queue.max_seconds_unlock" max_seconds = api.get_registry_record(registry_id) max_seconds = api.to_int(max_seconds, default=default) return max_seconds >= 30 and max_seconds or default
def get_max_retries(default=3): """Returns the number of retries before considering a task as failed """ registry_id = "senaite.queue.max_retries" max_retries = api.get_registry_record(registry_id) max_retries = api.to_int(max_retries, default=default) return max_retries >= 1 and max_retries or default
def wrapper(*args, **kwargs): if is_installed(): # Check if enabled from bika.lims import api reg_id = "senaite.sqlmultiplex.enabled" if api.get_registry_record(reg_id) is True: return func(*args, **kwargs)
def get_min_seconds(default=3): """Returns the minimum number of seconds to book per task """ registry_id = "senaite.queue.min_seconds_task" min_seconds = api.get_registry_record(registry_id) min_seconds = api.to_int(min_seconds, default=default) return min_seconds >= 1 and min_seconds or default
def is_patient_required(): """Checks if the patient is required """ required = api.get_registry_record("senaite.patient.require_patient") if not required: return False return True
def get_registry_records_by_keyword(keyword=None): """Get all the registry records (names and values) whose name contains the specified keyword or, if keyword is None, return all registry items :param keyword: The keyword that has to be contained in the record name :type keyword: str or None :returns: Dictionary mapping the names of the found records to its values """ portal_reg = ploneapi.portal.get_tool(name="portal_registry") found_registers = {} for record in portal_reg.records: if keyword is None: found_registers[record] = api.get_registry_record(record) elif keyword.lower() in record.lower(): found_registers[record] = api.get_registry_record(record) return found_registers
def get_patient_name_entry_mode(): """Returns the entry mode for patient name """ entry_mode = api.get_registry_record("senaite.patient.patient_entry_mode") if not entry_mode: # Default to firstname + fullname entry_mode = "parts" return entry_mode
def get_connection_configuration(self): """Returns a dict with the configuration for the SQL connection """ config = {} params = ["host", "port", "database", "user", "password"] for param in params: reg_id = "senaite.sqlmultiplex.{}".format(param) config.update({param: api.get_registry_record(reg_id)}) return config
def guard_publish(self): """Returns whether the sample can be published """ temp_mrn = self.context.isMedicalRecordTemporary() if temp_mrn: if not api.get_registry_record("senaite.patient.publish_temp_mrn"): return False return True
def get_default_template(self, default="senaite.lims:Default.pt"): """Returns the configured default template from the registry """ template = self.get_request_parameter("template") if self.template_exists(template): return template template = api.get_registry_record("senaite.impress.default_template") if template is None: return default return template
def guard_verify(self): """Returns whether the sample can be verified """ temp_mrn = self.context.isMedicalRecordTemporary() if temp_mrn: # Check whether users can verify samples with a temporary MRN if not api.get_registry_record("senaite.patient.verify_temp_mrn"): return False return True
def max_email_size(self): """Return the max. allowed email size in KB """ # check first if a registry record exists max_email_size = api.get_registry_record("senaite.core.max_email_size") if max_email_size is None: max_size = DEFAULT_MAX_EMAIL_SIZE if max_size < 0: max_email_size = 0 return max_size * 1024
def get_default_orientation(self, default="portrait"): """Returns the configured default orientation from the registry """ orientation = self.get_request_parameter("orientation") if orientation in ["portrait", "landscape"]: return orientation orientation = api.get_registry_record( "senaite.impress.default_orientation") if orientation is None: return default return orientation
def get_default_paperformat(self, default="A4"): """Returns the configured default paperformat from the registry """ paperformat = self.get_request_parameter("paperformat") if paperformat in self.get_paperformats(): return paperformat paperformat = api.get_registry_record( "senaite.impress.default_paperformat") if paperformat is None: return default return paperformat
def subject(self): """Returns the subject of the email """ email_subject = api.get_registry_record("senaite.panic.email_subject") client = self.sample.getClient() return self.context.translate( email_subject, mapping={ "sample_id": api.get_id(self.sample), "client_id": client.getClientID(), "client_sample_id": self.sample.getClientSampleID(), })
def __call__(self, r): # We want our key to be valid for 10 seconds only secs = time.time() + 10 token = "{}:{}".format(secs, self.username) # Encrypt the token using our symmetric auth key if not self.key: self.key = api.get_registry_record("senaite.queue.auth_key") auth_token = Fernet(str(self.key)).encrypt(token) # Modify and return the request r.headers["X-Queue-Auth-Token"] = auth_token return r
def get_server_url(): """Returns the url of the queue server if valid. None otherwise. """ url = _api.get_registry_record("senaite.queue.server") try: result = parse.urlparse(url) except: # noqa a convenient way to check if the url is valid return None # Validate the url is ok if not all([result.scheme, result.netloc, result.path]): return None url = "{}://{}{}".format(result.scheme, result.netloc, result.path) return url.strip("/")
def get_chunk_size(name_or_action=None): """Returns the number of items to process at once for the given task name :param name_or_action: task name or workflow action id :returns: the number of items from the task to process async at once :rtype: int """ chunk_size = api.get_registry_record("senaite.queue.default") chunk_size = api.to_int(chunk_size, 0) if chunk_size <= 0: # Queue disabled return 0 if name_or_action: # TODO Retrieve task-specific chunk-sizes via adapters pass if chunk_size < 0: chunk_size = 0 return chunk_size
def extractCredentials(self, request): # noqa camelCase """IExtractionPlugin implementation. Extracts login name from the request's "X-Queue-Auth-Token" header. This header contains an encrypted token with it's expiration date, together with the user name. Returns a dict with {'login': <username>} if: - current layer is ISenaiteQueueLayer, - the token can be decrypted, - the decrypted token contains both expiration and username and - the the token has not expired (expiration date) Returns an empty dict otherwise :param request: the HTTPRequest object to extract credentials from :return: a dict {"login": <username>} or empty dict """ # Check if request provides ISenaiteQueueLayer if not ISenaiteQueueLayer.providedBy(request): return {} # Read the magical header that contains the encrypted info auth_token = request.getHeader("X-Queue-Auth-Token") if not auth_token: return {} # Decrypt the auth_token key = api.get_registry_record("senaite.queue.auth_key") token = Fernet(str(key)).decrypt(auth_token) # Check if token is valid tokens = token.split(":") if len(tokens) < 2 or not api.is_floatable(tokens[0]): return {} # Check if token has expired expiration = api.to_float(tokens[0]) if expiration < time.time(): return {} user_id = "".join(tokens[1:]) return {"login": user_id}
def body(self): """Returns the body message of the email """ setup = api.get_setup() laboratory = setup.laboratory lab_address = "\n".join(laboratory.getPrintAddress()) analyses = map(self.to_str, self.get_analyses_in_panic(self.sample)) analyses = "\n-".join(analyses) # TODO more mappings here (custom body)! email_body = api.get_registry_record("senaite.panic.email_body") client = self.sample.getClient() return self.context.translate( email_body, mapping={ "sample_id": api.get_id(self.sample), "analyses": analyses, "lab_address": lab_address, "client_id": client.getClientID(), "client_sample_id": self.sample.getClientSampleID(), "sample_url": api.get_url(self.sample), })
def get_multiplexed_types(self): """Returns the list of portal types to be multiplexed """ reg_id = "senaite.sqlmultiplex.content_types" return api.get_registry_record(reg_id, [])
def store_multireports_individually(self): """Returns the configured setting from the registry """ store_individually = api.get_registry_record( "senaite.impress.store_multireports_individually") return store_individually
def get_developer_mode(self): """Returns the configured setting from the registry """ mode = api.get_registry_record("senaite.impress.developer_mode", False) return mode
def get_footer_text(self, escape=True): """Returns the footer text from the setup """ return api.get_registry_record("senaite.impress.footer")
def consume_task(): """Consumes a task from the queue, if any """ if not is_installed(): return info("Queue is not installed") host = _api.get_request().get("SERVER_URL") if not is_valid_zeo_host(host): return error("zeo host not set or not valid: {} [SKIP]".format(host)) consumer_thread = get_consumer_thread() if consumer_thread: # There is a consumer working already name = consumer_thread.getName() return info("Consumer running: {} [SKIP]".format(name)) logger.info("Queue client: {}".format(host)) # Server's queue URL server = api.get_server_url() # Check the status of the queue status = api.get_queue_status() if status not in ["resuming", "ready"]: return warn("Server is {} ({}) [SKIP]".format(status, server)) if api.is_queue_server(): message = [ "Server = Consumer: {}".format(server), "*******************************************************", "Client configured as both queue server and consumer.", "This is not suitable for productive environments!", "Change the Queue Server URL in SENAITE's control panel", "or setup another zeo client as queue consumer.", "Current URL: {}".format(server), "*******************************************************" ] logger.warn("\n".join(message)) # Pop next task to process consumer_id = host try: task = api.get_queue().pop(consumer_id) if not task: return info("Queue is empty or process undergoing [SKIP]") except Exception as e: return error("Cannot pop. {}: {}".format(type(e).__name__, str(e))) auth_key = _api.get_registry_record("senaite.queue.auth_key") kwargs = { "task_uid": task.task_uid, "task_username": task.username, "consumer_id": consumer_id, "base_url": _api.get_url(_api.get_portal()), "server_url": api.get_server_url(), "user_id": _api.get_current_user().id, "max_seconds": get_max_seconds(), "auth_key": auth_key, } name = "{}{}".format(CONSUMER_THREAD_PREFIX, int(time.time())) t = threading.Thread(name=name, target=process_task, kwargs=kwargs) t.start() return info("Consumer running: {} [SKIP]".format(CONSUMER_THREAD_PREFIX))