Module pipettin-piper.piper.datatools.jsondb

Functions

def load_datatools(controller: Controller)
Expand source code
def load_datatools(controller: Controller):
    if controller.database_tools:
        raise DataError("Database tools already set in controller to: " + str(controller.database_tools))
    # Set the databse_tools property to an instance of JsonObjects.
    controller.database_tools = JsonObjects(controller=controller)
    return controller.database_tools
def load_from_config(**db_config: dict)
Expand source code
def load_from_config(**db_config : dict):
    return JsonObjects(**db_config)

Classes

class JsonObjects (database_url=None,
database_name=None,
controller=None,
verbose=True,
env_file: str = None)
Expand source code
class JsonObjects(DataTools):
    """A class holding data about the machine, objects on it, and its configuration.
    Uses a JSON file as a data backend.
    """

    verbose: bool = False
    controller: Controller = None
    database_url: str = db_location
    database_name: str = 'pipettin'

    env_file: str = '../pipettin-gui/.env'
    """Path to the UI's '.env' file, containing DATABASE_NAME and DATABASE_URI variables."""

    def __init__(self,
                 database_url=None,
                 database_name=None,
                 controller=None,
                 verbose=True,
                 env_file:str=None):

        self.verbose = verbose

        # Default connection details.
        self.config = {"database": {
            "database_url": self.database_url,
            "database_name": self.database_name
        }}

        # Get the config from the controller, if any.
        if controller:
            self.controller = controller
            self.verbose = controller.verbose

            # Update connection details.
            self.config["database"].update(controller.config["database"])

            # Load variables from ".env".
            self.env_file = self.config.get("env_file", env_file)
            if self.env_file is not None:
                self.update_from_env_file(self.config, self.env_file)

        # Override connection details from init arguments.
        if database_url is not None:
            self.config["database"]["database_url"] = database_url
        if database_name is not None:
            self.config["database"]["database_name"] = database_name

        self.database_url = self.config["database"]["database_url"]
        self.database_name = self.config["database"]["database_name"]

        self.setup_db(self.database_url, self.database_name)

    # DATABASE SETUP ############
    def setup_db(self, database_url: str, database_name: str):
        """Load and configure a JSON-based database from a given URL.

        This method sets up a connection to a JSON-based database by loading JSON data from 
        the specified `database_url`. It initializes various collections within the database, 
        ensuring the specified `database_name` exists and can be accessed.

        Args:
            database_url (str): The file path or URL to the JSON file containing the database data.
            database_name (str): The name of the database to load from the JSON data.

        Raises:
            KeyError: Raised if the `database_name` or any required collection (e.g., protocols, 
                    workspaces, platforms) is missing from the loaded JSON data.
            Exception: Raised if any other issue occurs during loading or accessing the database, 
                    including issues with file access or malformed JSON content.
        """
        try:
            # Connect and select database name.
            logging.info(f"Loading JSON from: '{database_url}'")
            self.json = load_objects(database_url)
            # The databases in the MongoClient are accessed with a dict-style syntax.
            logging.info(f"Loaded database: '{database_name}'")
            self.db = self.json[database_name]

        except Exception as e:
            logging.error(f"Failed connect to the database or to load objects from it: {e}\n{traceback.print_exc()}")
            raise e

    # Main class methods ####

    #### POP METHODS ####

    def pop_content_by_idx(self, workspace_name: str, item_name: str, content_idx: int):
        """Remove a content from a platform item by index, deleting it form the DB."""
        try:
            workspace = self.getWorkspaceByName(workspace_name)
            item = self.getWorkspaceItemByName(workspace=workspace, item_name=item_name)
            # Referencing will remove it from the task class/workspace object class as well.
            # This is the desired (side)effect.
            popped_content = item["content"].pop(content_idx)
        except Exception as e:
            msg = f"Failed to pull content {content_idx}"
            msg += f" from item '{item_name}' in workspace '{workspace_name}'."
            logging.error(msg)
            raise DataError(msg) from e

        return popped_content

    #### GET METHODS ####

    def listProtocols(self):
        """
        Annoying function to get protocols from MongoDB as a list, log protocol names, and return them.
        """
        protocols = self.db['protocols']

        logging.debug(f"Found the following protocols: {[p['name'] for p in protocols]}")

        return protocols

    def listHlProtocols(self):
        """Method to get high-level protocols from MongoDB as a list, log protocol names, and return them."""

        hl_protocols = self.db['hLprotocols']

        logging.debug(f"Found the following high-level protocols: {[p['name'] for p in hl_protocols]}")

        return hl_protocols

    def listWorkspaces(self):
        """
        Annoying function to get workspaces from MongoDB as a list, log names, and return them.
        """
        workspaces = self.db['workspaces']

        logging.debug(f"\nFound the following workspaces: {[w['name'] for w in workspaces]}")

        return workspaces

    def listPlatforms(self):
        """
        Annoying function to get platforms from MongoDB as a list, log names, and return them.
        """
        platforms = self.db['platforms']

        logging.debug(f"Found the following platforms: {[pl['name'] for pl in platforms]}")

        return platforms

    def listContainers(self):
        """
        Annoying function to get 'containers' from MongoDB as a list, log names, and return them.
        """
        containers = self.db['containers']

        logging.debug(f"Found the following containers: {[t['name'] for t in containers]}")

        return containers

    def listTools(self):
        """
        Annoying function to get tools from MongoDB as a list, log names, and return them.
        """
        tools = self.db['tools']

        logging.debug(f"Found the following tools: {[t['name'] for t in tools]}")

        return tools

    def listSettings(self):
        """
        Annoying function to get 'settings' from MongoDB as a list, log names, and return them.
        """
        settings = self.db['settings']

        logging.debug(f"Found {len(settings)} set(s) of settings.")

        if settings is None:
            pass
        elif len(settings) > 1:
            logging.warning("More than one set of settings was retreived from the database.")

        return settings

    #### CUSTOM UPDATE METHODS ####

    def updateActionBy(self, action: dict, protocol_name: str,
                       new_data: dict, field: str = None,
                       find_by: str = "index"):
        """Update an action's data by its Object ID."""
        msg = f"Updating action with {find_by}={action[find_by]} " + \
              f"from protocol '{protocol_name}' with new_data={new_data} and field={field}"
        logging.info(msg)
        try:
            _, db_action, _ = self.getActionDataBy(action[find_by], id_field=find_by)
            if field is not None:
                # Field-specific update.
                db_action[field] = new_data
            else:
                # Non-specific update.
                db_action.update(new_data)
        except Exception as e:
            msg = f"Failed to update action by {find_by}={action[find_by]}."
            logging.error(msg)
            raise DataError(msg) from e

A class holding data about the machine, objects on it, and its configuration. Uses a JSON file as a data backend.

Ancestors

Subclasses

Class variables

var controller : Controller
var database_name : str
var database_url : str
var env_file : str

Path to the UI's '.env' file, containing DATABASE_NAME and DATABASE_URI variables.

var verbose : bool

Methods

def listContainers(self)
Expand source code
def listContainers(self):
    """
    Annoying function to get 'containers' from MongoDB as a list, log names, and return them.
    """
    containers = self.db['containers']

    logging.debug(f"Found the following containers: {[t['name'] for t in containers]}")

    return containers

Annoying function to get 'containers' from MongoDB as a list, log names, and return them.

def listHlProtocols(self)
Expand source code
def listHlProtocols(self):
    """Method to get high-level protocols from MongoDB as a list, log protocol names, and return them."""

    hl_protocols = self.db['hLprotocols']

    logging.debug(f"Found the following high-level protocols: {[p['name'] for p in hl_protocols]}")

    return hl_protocols

Method to get high-level protocols from MongoDB as a list, log protocol names, and return them.

def listPlatforms(self)
Expand source code
def listPlatforms(self):
    """
    Annoying function to get platforms from MongoDB as a list, log names, and return them.
    """
    platforms = self.db['platforms']

    logging.debug(f"Found the following platforms: {[pl['name'] for pl in platforms]}")

    return platforms

Annoying function to get platforms from MongoDB as a list, log names, and return them.

def listProtocols(self)
Expand source code
def listProtocols(self):
    """
    Annoying function to get protocols from MongoDB as a list, log protocol names, and return them.
    """
    protocols = self.db['protocols']

    logging.debug(f"Found the following protocols: {[p['name'] for p in protocols]}")

    return protocols

Annoying function to get protocols from MongoDB as a list, log protocol names, and return them.

def listSettings(self)
Expand source code
def listSettings(self):
    """
    Annoying function to get 'settings' from MongoDB as a list, log names, and return them.
    """
    settings = self.db['settings']

    logging.debug(f"Found {len(settings)} set(s) of settings.")

    if settings is None:
        pass
    elif len(settings) > 1:
        logging.warning("More than one set of settings was retreived from the database.")

    return settings

Annoying function to get 'settings' from MongoDB as a list, log names, and return them.

def listTools(self)
Expand source code
def listTools(self):
    """
    Annoying function to get tools from MongoDB as a list, log names, and return them.
    """
    tools = self.db['tools']

    logging.debug(f"Found the following tools: {[t['name'] for t in tools]}")

    return tools

Annoying function to get tools from MongoDB as a list, log names, and return them.

def listWorkspaces(self)
Expand source code
def listWorkspaces(self):
    """
    Annoying function to get workspaces from MongoDB as a list, log names, and return them.
    """
    workspaces = self.db['workspaces']

    logging.debug(f"\nFound the following workspaces: {[w['name'] for w in workspaces]}")

    return workspaces

Annoying function to get workspaces from MongoDB as a list, log names, and return them.

def setup_db(self, database_url: str, database_name: str)
Expand source code
def setup_db(self, database_url: str, database_name: str):
    """Load and configure a JSON-based database from a given URL.

    This method sets up a connection to a JSON-based database by loading JSON data from 
    the specified `database_url`. It initializes various collections within the database, 
    ensuring the specified `database_name` exists and can be accessed.

    Args:
        database_url (str): The file path or URL to the JSON file containing the database data.
        database_name (str): The name of the database to load from the JSON data.

    Raises:
        KeyError: Raised if the `database_name` or any required collection (e.g., protocols, 
                workspaces, platforms) is missing from the loaded JSON data.
        Exception: Raised if any other issue occurs during loading or accessing the database, 
                including issues with file access or malformed JSON content.
    """
    try:
        # Connect and select database name.
        logging.info(f"Loading JSON from: '{database_url}'")
        self.json = load_objects(database_url)
        # The databases in the MongoClient are accessed with a dict-style syntax.
        logging.info(f"Loaded database: '{database_name}'")
        self.db = self.json[database_name]

    except Exception as e:
        logging.error(f"Failed connect to the database or to load objects from it: {e}\n{traceback.print_exc()}")
        raise e

Load and configure a JSON-based database from a given URL.

This method sets up a connection to a JSON-based database by loading JSON data from the specified database_url. It initializes various collections within the database, ensuring the specified database_name exists and can be accessed.

Args

database_url : str
The file path or URL to the JSON file containing the database data.
database_name : str
The name of the database to load from the JSON data.

Raises

KeyError
Raised if the database_name or any required collection (e.g., protocols, workspaces, platforms) is missing from the loaded JSON data.
Exception
Raised if any other issue occurs during loading or accessing the database, including issues with file access or malformed JSON content.
def updateActionBy(self,
action: dict,
protocol_name: str,
new_data: dict,
field: str = None,
find_by: str = 'index')
Expand source code
def updateActionBy(self, action: dict, protocol_name: str,
                   new_data: dict, field: str = None,
                   find_by: str = "index"):
    """Update an action's data by its Object ID."""
    msg = f"Updating action with {find_by}={action[find_by]} " + \
          f"from protocol '{protocol_name}' with new_data={new_data} and field={field}"
    logging.info(msg)
    try:
        _, db_action, _ = self.getActionDataBy(action[find_by], id_field=find_by)
        if field is not None:
            # Field-specific update.
            db_action[field] = new_data
        else:
            # Non-specific update.
            db_action.update(new_data)
    except Exception as e:
        msg = f"Failed to update action by {find_by}={action[find_by]}."
        logging.error(msg)
        raise DataError(msg) from e

Update an action's data by its Object ID.

Inherited members