IRC-SPHERE/HyperStream

View on GitHub
hyperstream/meta_data/meta_data_manager.py

Summary

Maintainability
A
45 mins
Test Coverage
# The MIT License (MIT)
# Copyright (c) 2014-2017 University of Bristol
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.

from ..utils import Printable, MetaDataTree
from ..models import MetaDataModel

import logging
from mongoengine.context_managers import switch_db
from treelib.tree import NodeIDAbsentError


class MetaDataManager(Printable):
    def __init__(self):
        self.global_plate_definitions = MetaDataTree()
        self.global_plate_definitions.create_node(identifier="root")

        to_be_added = dict((i, d) for i, d, in enumerate(self.global_meta_data))
        passes = 0

        # Populate the global plate definitions from dict given in the database
        while len(to_be_added) > 0:
            passes += 1
            if passes > 1000:
                raise NodeIDAbsentError("Nodes absent for ids {}"
                                        .format(", ".join(map(lambda x: x['identifier'], to_be_added.values()))))
            items = list(to_be_added.items())
            for i, item in items:
                try:
                    self.global_plate_definitions.create_node(**item)
                    del to_be_added[i]
                except NodeIDAbsentError:
                    pass

        logging.info("Global plate definitions: ")
        logging.info(self.global_plate_definitions)

    @property
    def global_meta_data(self):
        """
        Get the global meta data, which will be stored in a tree structure

        :return: The global meta data
        """
        with switch_db(MetaDataModel, 'hyperstream'):
            return sorted(map(lambda x: x.to_dict(), MetaDataModel.objects),
                          key=lambda x: len(x['identifier'].split('.')),
                          reverse=True)

    def contains(self, identifier):
        """
        Determines if the meta data with the given identifier is in the database

        :param identifier: The identifier
        :return: Whether the identifier is present
        """
        return self.global_plate_definitions.contains(identifier)

    def insert(self, tag, identifier, parent, data):
        """
        Insert the given meta data into the database

        :param tag: The tag (equates to meta_data_id)
        :param identifier: The identifier (a combination of the meta_data_id and the plate value)
        :param parent: The parent plate identifier
        :param data: The data (plate value)
        :return: None
        """
        # First try to add it into the tree
        if self.global_plate_definitions.contains(identifier):
            raise KeyError("Identifier {} already exists in tree".format(identifier))

        self.global_plate_definitions.create_node(tag=tag, identifier=identifier, parent=parent, data=data)

        # Now try to add it into the database
        with switch_db(MetaDataModel, 'hyperstream'):
            meta_data = MetaDataModel(tag=tag, parent=parent, data=data)
            meta_data.save()

        logging.info("Meta data {} inserted".format(identifier))

    def delete(self, identifier):
        """
        Delete the meta data with the given identifier from the database

        :param identifier: The identifier
        :return: None
        """

        try:
            node = self.global_plate_definitions[identifier]
        except NodeIDAbsentError:
            logging.info("Meta data {} not present during deletion".format(identifier))
            return

        # First delete any children of the node: REMOVED as this seemed to be unreliable
        # It's now better to call delete_plate with delete_meta_data=True
        # for child in node.fpointer:
        #     self.delete(child)

        self.global_plate_definitions.remove_node(identifier)

        with switch_db(MetaDataModel, 'hyperstream'):
            meta_data = MetaDataModel.objects(tag=node.tag, data=node.data, parent=node.bpointer).first()
            if meta_data is not None:
                meta_data.delete()

        logging.info("Meta data {} deleted".format(identifier))