def __init__(self, **kwargs): given_dbgroup = kwargs.pop('dbgroup', None) if given_dbgroup is not None: # Check that there is no other parameter passed besides dbgroup if kwargs: raise ValueError("If you pass a dbgroups, you cannot pass any " "further parameter") if isinstance(given_dbgroup, (int, long)): dbgroup_res = DbGroup.query.filter_by(id=given_dbgroup).first() if not dbgroup_res: raise NotExistent("Group with pk={} does not exist".format( given_dbgroup)) self._dbgroup = dbgroup_res elif isinstance(given_dbgroup, DbGroup): self._dbgroup = given_dbgroup else: name = kwargs.pop('name', None) if name is None: raise ValueError("You have to specify a group name") group_type = kwargs.pop('type_string', "") # By default, an user group user = kwargs.pop('user', get_automatic_user()) description = kwargs.pop('description', "") if kwargs: raise ValueError("Too many parameters passed to Group, the " "unknown parameters are: {}".format(", ".join( kwargs.keys()))) self._dbgroup = DbGroup(name=name, description=description, user=user, type=group_type)
def __init__(self, **kwargs): from aiida.orm.backend import construct_backend super(Group, self).__init__() self._backend = construct_backend() given_dbgroup = kwargs.pop('dbgroup', None) if given_dbgroup is not None: # Check that there is no other parameter passed besides dbgroup if kwargs: raise ValueError("If you pass a dbgroups, you cannot pass any " "further parameter") if isinstance(given_dbgroup, (int, long)): dbgroup_res = DbGroup.query.filter_by(id=given_dbgroup).first() if not dbgroup_res: raise NotExistent("Group with pk={} does not exist".format( given_dbgroup)) dbgroup = dbgroup_res elif isinstance(given_dbgroup, DbGroup): dbgroup = given_dbgroup else: raise ValueError("Unrecognised inputs '{}'".format(kwargs)) else: name = kwargs.pop('name', None) if name is None: raise ValueError("You have to specify a group name") group_type = kwargs.pop('type_string', "") # By default, an user group # Get the user and extract the dbuser instance user = kwargs.pop('user', self._backend.users.get_automatic_user()) user = user.dbuser description = kwargs.pop('description', "") if kwargs: raise ValueError("Too many parameters passed to Group, the " "unknown parameters are: {}".format(", ".join( kwargs.keys()))) dbgroup = DbGroup(name=name, description=description, user=user, type=group_type) self._dbgroup = utils.ModelWrapper(dbgroup)
def __init__(self, backend, label, user, description='', type_string=''): """ Construct a new SQLA group :param backend: the backend to use :param label: the group label :param user: the owner of the group :param description: an optional group description :param type_string: an optional type for the group to contain """ type_check(user, users.SqlaUser) super().__init__(backend) dbgroup = DbGroup(label=label, description=description, user=user.dbmodel, type_string=type_string) self._dbmodel = utils.ModelWrapper(dbgroup)
class Group(AbstractGroup): def __init__(self, **kwargs): given_dbgroup = kwargs.pop('dbgroup', None) if given_dbgroup is not None: # Check that there is no other parameter passed besides dbgroup if kwargs: raise ValueError("If you pass a dbgroups, you cannot pass any " "further parameter") if isinstance(given_dbgroup, (int, long)): dbgroup_res = DbGroup.query.filter_by(id=given_dbgroup).first() if not dbgroup_res: raise NotExistent("Group with pk={} does not exist".format( given_dbgroup)) self._dbgroup = dbgroup_res elif isinstance(given_dbgroup, DbGroup): self._dbgroup = given_dbgroup else: name = kwargs.pop('name', None) if name is None: raise ValueError("You have to specify a group name") group_type = kwargs.pop('type_string', "") # By default, an user group user = kwargs.pop('user', get_automatic_user()) description = kwargs.pop('description', "") if kwargs: raise ValueError("Too many parameters passed to Group, the " "unknown parameters are: {}".format( ", ".join(kwargs.keys()))) self._dbgroup = DbGroup(name=name, description=description, user=user, type=group_type) @property def name(self): return self._dbgroup.name @property def description(self): return self._dbgroup.description @description.setter def description(self, value): self._dbgroup.description = value # Update the entry in the DB, if the group is already stored if self.is_stored: self._dbgroup.save() @property def type_string(self): return self._dbgroup.type @property def user(self): return self._dbgroup.user @property def dbgroup(self): return self._dbgroup @property def pk(self): return self._dbgroup.id @property def id(self): return self._dbgroup.id @property def uuid(self): return unicode(self._dbgroup.uuid) def __int__(self): if self._to_be_stored: return None else: return self._dbnode.id @property def is_stored(self): return self.pk is not None def store(self): if not self.is_stored: try: self._dbgroup.save(commit=True) except SQLAlchemyError as ex: print ex.message raise UniquenessError("A group with the same name (and of the " "same type) already " "exists, unable to store") return self def add_nodes(self, nodes): if not self.is_stored: raise ModificationNotAllowed("Cannot add nodes to a group before " "storing") from aiida.orm.implementation.sqlalchemy.node import Node from aiida.backends.sqlalchemy import get_scoped_session session = get_scoped_session() # First convert to a list if isinstance(nodes, (Node, DbNode)): nodes = [nodes] if isinstance(nodes, basestring) or not isinstance( nodes, collections.Iterable): raise TypeError("Invalid type passed as the 'nodes' parameter to " "add_nodes, can only be a Node, DbNode, or a list " "of such objects, it is instead {}".format( str(type(nodes)))) list_nodes = [] for node in nodes: if not isinstance(node, (Node, DbNode)): raise TypeError("Invalid type of one of the elements passed " "to add_nodes, it should be either a Node or " "a DbNode, it is instead {}".format( str(type(node)))) if node.id is None: raise ValueError("At least one of the provided nodes is " "unstored, stopping...") if isinstance(node, Node): to_add = node.dbnode else: to_add = node if to_add not in self._dbgroup.dbnodes: # ~ list_nodes.append(to_add) self._dbgroup.dbnodes.append(to_add) session.commit() # ~ self._dbgroup.dbnodes.extend(list_nodes) @property def nodes(self): class iterator(object): def __init__(self, dbnodes): self.dbnodes = dbnodes self.generator = self._genfunction() def _genfunction(self): for n in self.dbnodes: yield n.get_aiida_class() def __iter__(self): return self def __len__(self): return len(self.dbnodes) # For future python-3 compatibility def __next__(self): return self.next() def next(self): return next(self.generator) return iterator(self._dbgroup.dbnodes.all()) def remove_nodes(self, nodes): if not self.is_stored: raise ModificationNotAllowed("Cannot remove nodes from a group " "before storing") from aiida.orm.implementation.sqlalchemy.node import Node # First convert to a list if isinstance(nodes, (Node, DbNode)): nodes = [nodes] if isinstance(nodes, basestring) or not isinstance( nodes, collections.Iterable): raise TypeError("Invalid type passed as the 'nodes' parameter to " "remove_nodes, can only be a Node, DbNode, or a " "list of such objects, it is instead {}".format( str(type(nodes)))) list_nodes = [] for node in nodes: if not isinstance(node, (Node, DbNode)): raise TypeError("Invalid type of one of the elements passed " "to add_nodes, it should be either a Node or " "a DbNode, it is instead {}".format( str(type(node)))) if node.id is None: raise ValueError("At least one of the provided nodes is " "unstored, stopping...") if isinstance(node, Node): node = node.dbnode # If we don't check first, SqlA might issue a DELETE statement for # an unexisting key, resulting in an error if node in self._dbgroup.dbnodes: list_nodes.append(node) for node in list_nodes: self._dbgroup.dbnodes.remove(node) sa.get_scoped_session().commit() @classmethod def query(cls, name=None, type_string="", pk=None, uuid=None, nodes=None, user=None, node_attributes=None, past_days=None, name_filters=None, **kwargs): from aiida.orm.implementation.sqlalchemy.node import Node session = sa.get_scoped_session() filters = [] if name is not None: filters.append(DbGroup.name == name) if type_string is not None: filters.append(DbGroup.type == type_string) if pk is not None: filters.append(DbGroup.id == pk) if uuid is not None: filters.append(DbGroup.uuid == uuid) if past_days is not None: filters.append(DbGroup.time >= past_days) if nodes: if not isinstance(nodes, collections.Iterable): nodes = [nodes] if not all(map(lambda n: isinstance(n, (Node, DbNode)), nodes)): raise TypeError("At least one of the elements passed as " "nodes for the query on Group is neither " "a Node nor a DbNode") # In the case of the Node orm from Sqlalchemy, there is an id # property on it. sub_query = (session.query(table_groups_nodes).filter( table_groups_nodes.c["dbnode_id"].in_( map(lambda n: n.id, nodes)), table_groups_nodes.c["dbgroup_id"] == DbGroup.id ).exists()) filters.append(sub_query) if user: if isinstance(user, basestring): filters.append(DbGroup.user.has(email=user)) else: # This should be a DbUser filters.append(DbGroup.user == user) if name_filters: for (k, v) in name_filters.iteritems(): if not v: continue if k == "startswith": filters.append(DbGroup.name.like("{}%".format(v))) elif k == "endswith": filters.append(DbGroup.name.like("%{}".format(v))) elif k == "contains": filters.append(DbGroup.name.like("%{}%".format(v))) if node_attributes: # TODO SP: IMPLEMENT THIS pass # TODO SP: handle **kwargs groups = (session.query(DbGroup.id).filter(*filters) .order_by(DbGroup.id).distinct().all()) return [cls(dbgroup=g[0]) for g in groups] def delete(self): session = sa.get_scoped_session() if self.pk is not None: session.delete(self._dbgroup) session.commit() new_group = copy(self._dbgroup) make_transient(new_group) new_group.id = None self._dbgroup = new_group
class Group(AbstractGroup): def __init__(self, **kwargs): given_dbgroup = kwargs.pop('dbgroup', None) if given_dbgroup is not None: # Check that there is no other parameter passed besides dbgroup if kwargs: raise ValueError("If you pass a dbgroups, you cannot pass any " "further parameter") if isinstance(given_dbgroup, (int, long)): dbgroup_res = DbGroup.query.filter_by(id=given_dbgroup).first() if not dbgroup_res: raise NotExistent("Group with pk={} does not exist".format( given_dbgroup)) self._dbgroup = dbgroup_res elif isinstance(given_dbgroup, DbGroup): self._dbgroup = given_dbgroup else: name = kwargs.pop('name', None) if name is None: raise ValueError("You have to specify a group name") group_type = kwargs.pop('type_string', "") # By default, an user group user = kwargs.pop('user', get_automatic_user()) description = kwargs.pop('description', "") if kwargs: raise ValueError("Too many parameters passed to Group, the " "unknown parameters are: {}".format(", ".join( kwargs.keys()))) self._dbgroup = DbGroup(name=name, description=description, user=user, type=group_type) @property def name(self): return self._dbgroup.name @property def description(self): return self._dbgroup.description @description.setter def description(self, value): self._dbgroup.description = value # Update the entry in the DB, if the group is already stored if self.is_stored: self._dbgroup.save() @property def type_string(self): return self._dbgroup.type @property def user(self): return self._dbgroup.user @property def dbgroup(self): return self._dbgroup @property def pk(self): return self._dbgroup.id @property def id(self): return self._dbgroup.id @property def uuid(self): return unicode(self._dbgroup.uuid) def __int__(self): if self._to_be_stored: return None else: return self._dbnode.id @property def is_stored(self): return self.pk is not None def store(self): if not self.is_stored: try: self._dbgroup.save(commit=True) except SQLAlchemyError as ex: print ex.message raise UniquenessError("A group with the same name (and of the " "same type) already " "exists, unable to store") return self def add_nodes(self, nodes, skip_orm=False, batch_size=5000): """ :param nodes: See the description of the abstract method that it extends :param skip_orm: When the flag is on, the SQLA ORM is skipped and a RAW SQL statement is issued (to improove speed). :param batch_size: The maximum number of nodes added per SQL query when skip_orm=True. """ if not self.is_stored: raise ModificationNotAllowed("Cannot add nodes to a group before " "storing") if skip_orm and batch_size <= 0: raise ValueError("batch_size should be a positive nunber") from aiida.orm.implementation.sqlalchemy.node import Node from aiida.backends.sqlalchemy import get_scoped_session session = get_scoped_session() # First convert to a list if isinstance(nodes, (Node, DbNode)): nodes = [nodes] if isinstance( nodes, basestring) or not isinstance(nodes, collections.Iterable): raise TypeError("Invalid type passed as the 'nodes' parameter to " "add_nodes, can only be a Node, DbNode, or a list " "of such objects, it is instead {}".format( str(type(nodes)))) # In the following list we store the the group,node pairs that will # be used for the non-ORM insert ins_list = list() insert_txt = "" node_count = 0 for node in nodes: if not isinstance(node, (Node, DbNode)): raise TypeError("Invalid type of one of the elements passed " "to add_nodes, it should be either a Node or " "a DbNode, it is instead {}".format( str(type(node)))) if node.id is None: raise ValueError("At least one of the provided nodes is " "unstored, stopping...") if isinstance(node, Node): to_add = node.dbnode else: to_add = node # If we would like to skip the ORM, we just populate the list containing # the group/node pairs to be inserted if skip_orm: # We keep the nodes in bathes that should be inserted one by one to # the group to avoid creating very big SQL statements insert_txt += "({}, {}), ".format(node.id, self.id) if node_count < batch_size: node_count += 1 else: node_count = 0 # Clean the end of the text insert_txt = insert_txt[:-2] # Keep it and reset the string for the next batch ins_list.append(insert_txt) insert_txt = "" # Otherwise follow the traditional approach for else: if to_add not in self._dbgroup.dbnodes: self._dbgroup.dbnodes.append(to_add) # Here we do the final insert for the non-ORM case if skip_orm: # Take care of the latest batch if it was not added to the list if len(insert_txt) > 0: insert_txt = insert_txt[:-2] ins_list.append(insert_txt) for ins_item in ins_list: statement = """INSERT INTO db_dbgroup_dbnodes(dbnode_id, dbgroup_id) VALUES {} ON CONFLICT DO NOTHING;""".format(ins_item) session.execute(statement) # #### The following code can be used to generate similar to the SQL statement shown above. # #### The difference is that it will create an SQL statement per added node resulting to a # #### performance degradation of ~50% comparing the above SQL statement that will # #### contain all nodes in one statement. # #### We don't use the following SQLA code for the moment because the "on_conflict_do_nothing" # #### is not supported by the SQLA version (1.0.x) that we use at AiiDA 0.12.x. # from sqlalchemy.dialects.postgresql import insert # from aiida.backends.sqlalchemy.models.group import table_groups_nodes # # Create the insert statement and update the relationship table # insert_statement = insert(table_groups_nodes).values(ins_dict) # session.execute(insert_statement.on_conflict_do_nothing(index_elements=['dbnode_id', 'dbgroup_id'])) session.commit() @property def nodes(self): class iterator(object): def __init__(self, dbnodes): self.dbnodes = dbnodes self.generator = self._genfunction() def _genfunction(self): for n in self.dbnodes: yield n.get_aiida_class() def __iter__(self): return self def __len__(self): return len(self.dbnodes) # For future python-3 compatibility def __next__(self): return self.next() def next(self): return next(self.generator) return iterator(self._dbgroup.dbnodes.all()) def remove_nodes(self, nodes): if not self.is_stored: raise ModificationNotAllowed("Cannot remove nodes from a group " "before storing") from aiida.orm.implementation.sqlalchemy.node import Node # First convert to a list if isinstance(nodes, (Node, DbNode)): nodes = [nodes] if isinstance( nodes, basestring) or not isinstance(nodes, collections.Iterable): raise TypeError("Invalid type passed as the 'nodes' parameter to " "remove_nodes, can only be a Node, DbNode, or a " "list of such objects, it is instead {}".format( str(type(nodes)))) list_nodes = [] for node in nodes: if not isinstance(node, (Node, DbNode)): raise TypeError("Invalid type of one of the elements passed " "to add_nodes, it should be either a Node or " "a DbNode, it is instead {}".format( str(type(node)))) if node.id is None: raise ValueError("At least one of the provided nodes is " "unstored, stopping...") if isinstance(node, Node): node = node.dbnode # If we don't check first, SqlA might issue a DELETE statement for # an unexisting key, resulting in an error if node in self._dbgroup.dbnodes: list_nodes.append(node) for node in list_nodes: self._dbgroup.dbnodes.remove(node) sa.get_scoped_session().commit() @classmethod def query(cls, name=None, type_string="", pk=None, uuid=None, nodes=None, user=None, node_attributes=None, past_days=None, name_filters=None, **kwargs): from aiida.orm.implementation.sqlalchemy.node import Node session = sa.get_scoped_session() filters = [] if name is not None: filters.append(DbGroup.name == name) if type_string is not None: filters.append(DbGroup.type == type_string) if pk is not None: filters.append(DbGroup.id == pk) if uuid is not None: filters.append(DbGroup.uuid == uuid) if past_days is not None: filters.append(DbGroup.time >= past_days) if nodes: if not isinstance(nodes, collections.Iterable): nodes = [nodes] if not all(map(lambda n: isinstance(n, (Node, DbNode)), nodes)): raise TypeError("At least one of the elements passed as " "nodes for the query on Group is neither " "a Node nor a DbNode") # In the case of the Node orm from Sqlalchemy, there is an id # property on it. sub_query = (session.query(table_groups_nodes).filter( table_groups_nodes.c["dbnode_id"].in_( map(lambda n: n.id, nodes)), table_groups_nodes.c["dbgroup_id"] == DbGroup.id).exists()) filters.append(sub_query) if user: if isinstance(user, basestring): filters.append(DbGroup.user.has(email=user)) else: # This should be a DbUser filters.append(DbGroup.user == user) if name_filters: for (k, v) in name_filters.iteritems(): if not v: continue if k == "startswith": filters.append(DbGroup.name.like("{}%".format(v))) elif k == "endswith": filters.append(DbGroup.name.like("%{}".format(v))) elif k == "contains": filters.append(DbGroup.name.like("%{}%".format(v))) if node_attributes: # TODO SP: IMPLEMENT THIS pass # TODO SP: handle **kwargs groups = (session.query(DbGroup.id).filter(*filters).order_by( DbGroup.id).distinct().all()) return [cls(dbgroup=g[0]) for g in groups] def delete(self): session = sa.get_scoped_session() if self.pk is not None: session.delete(self._dbgroup) session.commit() new_group = copy(self._dbgroup) make_transient(new_group) new_group.id = None self._dbgroup = new_group