Ejemplo n.º 1
0
    def get_upf_groups(cls, filter_elements=None, user=None):
        """Return all names of groups of type UpfFamily, possibly with some filters.

        :param filter_elements: A string or a list of strings.
            If present, returns only the groups that contains one UPF for every element present in the list. The default
            is `None`, meaning that all families are returned.
        :param user: if None (default), return the groups for all users.
            If defined, it should be either a `User` instance or the user email.
        :return: list of `Group` entities of type UPF.
        """
        from aiida.orm import UpfFamily
        from aiida.orm import QueryBuilder
        from aiida.orm import User

        builder = QueryBuilder()
        builder.append(UpfFamily, tag='group', project='*')

        if user:
            builder.append(User,
                           filters={'email': {
                               '==': user
                           }},
                           with_group='group')

        if isinstance(filter_elements, str):
            filter_elements = [filter_elements]

        if filter_elements is not None:
            builder.append(
                UpfData,
                filters={'attributes.element': {
                    'in': filter_elements
                }},
                with_group='group')

        builder.order_by({UpfFamily: {'id': 'asc'}})

        return [group for group, in builder.all()]
Ejemplo n.º 2
0
def get_data_aiida(quantitites):
    """Query the AiiDA database for a list of quantities (other appl results).
    Return list of entries like [mat_id, mat_name, mat_class, quantity-1, quantity-2, ..., quantity-n].
    """

    qb = QueryBuilder()
    qb.append(Group,
              filters={'label': {
                  'like': GROUP_DIR + "%"
              }},
              tag='g',
              project=[
                  'extras.mat_id', 'extras.name_conventional',
                  'extras.class_material'
              ])

    for q in quantitites:
        qb.append(Dict,
                  project=['attributes.{}'.format(q['key'])],
                  filters={'extras.{}'.format(TAG_KEY): q['dict']},
                  with_group='g')

    return qb.all()
Ejemplo n.º 3
0
def node_show(nodes, print_groups):
    """Show generic information on one or more nodes."""
    from aiida.cmdline.utils.common import get_node_info

    for node in nodes:
        # pylint: disable=fixme
        # TODO: Add a check here on the node type, otherwise it might try to access
        # attributes such as code which are not necessarily there
        echo.echo(get_node_info(node))

        if print_groups:
            from aiida.orm.querybuilder import QueryBuilder
            from aiida.orm.groups import Group
            from aiida.orm import Node  # pylint: disable=redefined-outer-name

            # pylint: disable=invalid-name
            qb = QueryBuilder()
            qb.append(Node, tag='node', filters={'id': {'==': node.pk}})
            qb.append(Group,
                      tag='groups',
                      with_node='node',
                      project=['id', 'label', 'type_string'])

            echo.echo('#### GROUPS:')

            if qb.count() == 0:
                echo.echo(f'Node {node.pk} does not belong to any group')
            else:
                echo.echo(f'Node {node.pk} belongs to the following groups:')
                res = qb.iterdict()
                table = [(gr['groups']['id'], gr['groups']['label'],
                          gr['groups']['type_string']) for gr in res]
                table.sort()

                echo.echo(
                    tabulate.tabulate(table,
                                      headers=['PK', 'Label', 'Group type']))
Ejemplo n.º 4
0
def querry_for_ref_structure(element_string):
    """
    This methods finds StructureData nodes with the following extras:
    extra.type = 'bulk', # Should be done by looking at pbc, but I could not
    get querry to work.
    extra.specific = 'reference',
    'extra.elemental' = True,
    extra.structure = element_string

    param: element_string: string of an element
    return: the latest StructureData node that was found
    """

    #query db
    q = QueryBuilder()
    q.append(StructureData,
             filters={
                 'extras.type': {
                     '==': 'bulk'
                 },
                 'extras.specification': {
                     '==': 'reference'
                 },
                 'extras.elemental': {
                     '==': True
                 },
                 'extras.element': {
                     '==': element_string
                 }
             })
    q.order_by({StructureData: 'ctime'})  #always use the most recent
    structures = q.all()

    if structures:
        return structures[-1][0]
    else:
        return None
Ejemplo n.º 5
0
def _build_query(order_by=None, limit=None, past_days=None):
    from aiida.orm.querybuilder import QueryBuilder
    from aiida.orm.calculation import Calculation
    import aiida.utils.timezone as timezone
    import datetime
    from aiida.orm.mixins import SealableMixin
    _SEALED_ATTRIBUTE_KEY = 'attributes.{}'.format(SealableMixin.SEALED_KEY)

    # The things that we want to get out
    calculation_projections = \
        ['id', 'ctime', 'type', _SEALED_ATTRIBUTE_KEY]

    now = timezone.now()

    # The things to filter by
    calculation_filters = {}

    if past_days is not None:
        n_days_ago = now - datetime.timedelta(days=past_days)
        calculation_filters['ctime'] = {'>': n_days_ago}

    qb = QueryBuilder()

    # ORDER
    if order_by is not None:
        qb.order_by({'calculation': [order_by]})

    # LIMIT
    if limit is not None:
        qb.limit(limit)

    # Build the quiery
    qb.append(cls=Calculation,
              filters=calculation_filters,
              project=calculation_projections,
              tag='calculation')
    return qb.iterdict()
Ejemplo n.º 6
0
def mock_vasp(aiida_env, localhost):
    """Points to a mock-up of a VASP executable."""
    from aiida.orm import Code
    from aiida.orm.querybuilder import QueryBuilder
    query_builder = QueryBuilder()
    query_builder.append(Code, tag='code')
    query_builder.add_filter('code', {'label': {'==': 'mock-vasp'}})
    query_results = query_builder.all()
    if query_results:
        code = query_results[0][0]
    else:
        os_env = os.environ.copy()
        if not localhost.pk:
            localhost.store()
        mock_vasp_path = sp.check_output(['which', 'mock-vasp'],
                                         env=os_env).strip()
        code = Code()
        code.label = 'mock-vasp'
        code.description = 'Mock VASP for tests'
        code.set_remote_computer_exec((localhost, mock_vasp_path))
        code.set_input_plugin_name('vasp.vasp')
        aiidapath = py_path.local(aiida_env.root_dir).join('.aiida')
        code.set_prepend_text('export AIIDA_PATH={}'.format(aiidapath))
    return code
Ejemplo n.º 7
0
def _retrieve_basis_sets(files, stop_if_existing):
    """ get existing basis sets or create if not

    :param files: list of basis set file paths
    :param stop_if_existing: if True, check for the md5 of the files and,
        if the file already exists in the DB, raises a MultipleObjectsError.
        If False, simply adds the existing BasisSetData node to the group.
    :return:
    """
    from aiida.orm.querybuilder import QueryBuilder

    basis_and_created = []
    for f in files:
        _, content = parse_basis(f)
        md5sum = md5_from_string(content)
        qb = QueryBuilder()
        qb.append(BasisSetData, filters={'attributes.md5': {'==': md5sum}})
        existing_basis = qb.first()

        if existing_basis is None:
            # return the basis set data instances, not stored
            basisset, created = BasisSetData.get_or_create(f,
                                                           use_first=True,
                                                           store_basis=False)
            # to check whether only one basis set per element exists
            # NOTE: actually, created has the meaning of "to_be_created"
            basis_and_created.append((basisset, created))
        else:
            if stop_if_existing:
                raise ValueError("A Basis Set with identical MD5 to "
                                 " {} cannot be added with stop_if_existing"
                                 "".format(f))
            existing_basis = existing_basis[0]
            basis_and_created.append((existing_basis, False))

    return basis_and_created
Ejemplo n.º 8
0
def get_data_aiida(inp_list):
    """Query the AiiDA database: find info in the README.
    
    Note: Removing the group filter speeds up queries on 70k COFs from ~10s to ~1s.
    """

    qb = QueryBuilder()
    qb.append(Group, filters={'label': {'like': 'group_%'}}, tag='group')
    qb.append(CifData,
              with_group='group',
              filters={'extras.group_tag': 'orig_cif'},
              project=['uuid', 'label'])

    for inp in inp_list:
        if 'henry_coefficient_average' in inp:
            proj = 'henry_coefficient_average'  # take out _co2, _n2, _ht
        else:
            proj = inp
        qb.append(Dict,
                  with_group='group',
                  filters={'extras.group_tag': get_tag[inp]},
                  project=['attributes.{}'.format(proj)])

    return qb.all()
def preprocess_spm_calcs(
        workchain_list=['STMWorkChain', 'PdosWorkChain', 'AfmWorkChain']):
    qb = QueryBuilder()
    qb.append(WorkCalculation,
              filters={
                  'attributes._process_label': {
                      'in': workchain_list
                  },
                  'or': [
                      {
                          'extras': {
                              '!has_key': 'preprocess_version'
                          }
                      },
                      {
                          'extras.preprocess_version': {
                              '<': PREPROCESS_VERSION
                          }
                      },
                  ],
              })
    qb.order_by({WorkCalculation: {'ctime': 'asc'}})

    for m in qb.all():
        n = m[0]
        ## ---------------------------------------------------------------
        ## calculation not finished
        if not n.is_sealed:
            print("Skipping underway workchain PK %d" % n.pk)
            continue
        calc_states = [out.get_state() for out in n.get_outputs()]
        if 'WITHSCHEDULER' in calc_states:
            print("Skipping underway workchain PK %d" % n.pk)
            continue
        ## ---------------------------------------------------------------

        if 'obsolete' not in n.get_extras():
            n.set_extra('obsolete', False)
        if n.get_extra('obsolete'):
            continue

        wc_name = n.get_attrs()['_process_label']

        try:
            if not all(
                [calc.get_state() == 'FINISHED' for calc in n.get_outputs()]):
                raise (Exception("Not all calculations are 'FINISHED'"))

            preprocess_one(n)
            print("Preprocessed PK %d (%s)" % (n.pk, wc_name))

            n.set_extra('preprocess_successful', True)
            n.set_extra('preprocess_version', PREPROCESS_VERSION)

            if 'preprocess_error' in n.get_extras():
                n.del_extra('preprocess_error')

        except Exception as e:
            n.set_extra('preprocess_successful', False)
            n.set_extra('preprocess_error', str(e))
            n.set_extra('preprocess_version', PREPROCESS_VERSION)
            print("Failed to preprocess PK %d (%s): %s" % (n.pk, wc_name, e))
Ejemplo n.º 10
0
parser = argparse.ArgumentParser()
parser.add_argument("code",
                    help="code and machine where you would like to run")
parser.add_argument("json_hpc", help="json file with HPC parameters")
parser.add_argument("json_pw", help="json file with PW parameters")
args = parser.parse_args()

StructureData = DataFactory('structure')
UpfData = DataFactory('upf')
ParameterData = DataFactory('parameter')
KpointsData = DataFactory('array.kpoints')

with open(args.json_hpc) as data_file:
    json_hpc = json.load(data_file)

qb = QueryBuilder()
qb.append(JobCalculation, tag="mycalculation", project=["*"])
qb.append(Group,
          filters={"name": json_hpc["query_group"]},
          group_of="mycalculation")
calcs_list = qb.all()

pseudo_family = json_hpc['pseudo']
structures_wf = []
kpoints_wf = []
pw_parameters_wf = []
hpc_workflow_params = {}
keys = []
count = 0

#for i in calcs_list[0:1]:
Ejemplo n.º 11
0
    def test_querybuilder_classifications(self):
        """
        This tests the classifications of the QueryBuilder u. the django backend.
        """
        from aiida.backends.querybuild.dummy_model import (
            DbNode, DbUser, DbComputer,
            DbGroup,
        )
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm.utils import (DataFactory, CalculationFactory)
        from aiida.orm.data.structure import StructureData
        from aiida.orm.implementation.django.node import Node
        from aiida.orm import Group, User, Node, Computer, Data, Calculation
        from aiida.common.exceptions import InputValidationError
        qb = QueryBuilder()

        with self.assertRaises(InputValidationError):
            qb._get_ormclass(None, 'data')
        with self.assertRaises(InputValidationError):
            qb._get_ormclass(None, 'data.Data')
        with self.assertRaises(InputValidationError):
            qb._get_ormclass(None, '.')

        for cls, clstype, query_type_string in (
                qb._get_ormclass(StructureData, None),
                qb._get_ormclass(None, 'data.structure.StructureData.'),
        ):
            self.assertEqual(clstype, 'data.structure.StructureData.')
            self.assertTrue(issubclass(cls, DbNode))
            self.assertEqual(clstype, 'data.structure.StructureData.')
            self.assertEqual(query_type_string,
                             StructureData._query_type_string)

        for cls, clstype, query_type_string in (
                qb._get_ormclass(Node, None),
                qb._get_ormclass(DbNode, None),
                qb._get_ormclass(None, '')
        ):
            self.assertEqual(clstype, Node._plugin_type_string)
            self.assertEqual(query_type_string, Node._query_type_string)
            self.assertTrue(issubclass(cls, DbNode))

        for cls, clstype, query_type_string in (
                qb._get_ormclass(DbGroup, None),
                qb._get_ormclass(Group, None),
                qb._get_ormclass(None, 'group'),
                qb._get_ormclass(None, 'Group'),
        ):
            self.assertEqual(clstype, 'group')
            self.assertEqual(query_type_string, None)
            self.assertTrue(issubclass(cls, DbGroup))

        for cls, clstype, query_type_string in (
                qb._get_ormclass(DbUser, None),
                qb._get_ormclass(DbUser, None),
                qb._get_ormclass(None, "user"),
                qb._get_ormclass(None, "User"),
        ):
            self.assertEqual(clstype, 'user')
            self.assertEqual(query_type_string, None)
            self.assertTrue(issubclass(cls, DbUser))

        for cls, clstype, query_type_string in (
                qb._get_ormclass(DbComputer, None),
                qb._get_ormclass(Computer, None),
                qb._get_ormclass(None, 'computer'),
                qb._get_ormclass(None, 'Computer'),
        ):
            self.assertEqual(clstype, 'computer')
            self.assertEqual(query_type_string, None)
            self.assertTrue(issubclass(cls, DbComputer))

        for cls, clstype, query_type_string in (
                qb._get_ormclass(Data, None),
                qb._get_ormclass(None, 'data.Data.'),
        ):
            self.assertEqual(clstype, Data._plugin_type_string)
            self.assertEqual(query_type_string, Data._query_type_string)
            self.assertTrue(issubclass(cls, DbNode))
Ejemplo n.º 12
0
    def test_simple_query_django_2(self):
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm import Node
        from datetime import datetime
        from aiida.backends.querybuild.dummy_model import (
            DbNode, DbLink, DbAttribute, session
        )

        n0 = DbNode(
            label='hello',
            type='',
            description='', user_id=1,
        )
        n1 = DbNode(
            label='foo',
            type='',
            description='I am FoO', user_id=2,
        )
        n2 = DbNode(
            label='bar',
            type='',
            description='I am BaR', user_id=3,
        )

        DbAttribute(
            key='foo',
            datatype='txt',
            tval='bar',
            dbnode=n0
        )

        l1 = DbLink(input=n0, output=n1, label='random_1', type='')
        l2 = DbLink(input=n1, output=n2, label='random_2', type='')

        session.add_all([n0, n1, n2, l1, l2])


        qb1 = QueryBuilder()
        qb1.append(
            DbNode,
            filters={
                'label': 'hello',
            }
        )
        self.assertEqual(len(list(qb1.all())), 1)

        qh = {
            'path': [
                {
                    'cls': Node,
                    'tag': 'n1'
                },
                {
                    'cls': Node,
                    'tag': 'n2',
                    'output_of': 'n1'
                }
            ],
            'filters': {
                'n1': {
                    'label': {'ilike': '%foO%'},
                },
                'n2': {
                    'label': {'ilike': 'bar%'},
                }
            },
            'project': {
                'n1': ['id', 'uuid', 'ctime', 'label'],
                'n2': ['id', 'description', 'label'],
            }
        }

        qb2 = QueryBuilder(**qh)

        resdict = qb2.dict()
        self.assertEqual(len(resdict), 1)
        resdict = resdict[0]
        self.assertTrue(isinstance(resdict['n1']['ctime'], datetime))
        self.assertEqual(resdict['n2']['label'], 'bar')


        qh = {
            'path': [
                {
                    'cls': Node,
                    'label': 'n1'
                },
                {
                    'cls': Node,
                    'label': 'n2',
                    'output_of': 'n1'
                }
            ],
            'filters': {
                'n1--n2': {'label': {'like': '%_2'}}
            }
        }
        qb = QueryBuilder(**qh)
        self.assertEqual(qb.count(), 1)

        # Test the hashing:
        query1 = qb.get_query()
        qb.add_filter('n2', {'label': 'nonexistentlabel'})
        self.assertEqual(qb.count(), 0)
        query2 = qb.get_query()
        query3 = qb.get_query()

        self.assertTrue(id(query1) != id(query2))
        self.assertTrue(id(query2) == id(query3))
Ejemplo n.º 13
0
def create(outfile, computers, groups, nodes, group_names, input_forward,
           create_reversed, return_reversed, call_reversed, overwrite,
           archive_format):
    """
    Export nodes and groups of nodes to an archive file for backup or sharing purposes
    """
    import sys
    from aiida.backends.utils import load_dbenv, is_dbenv_loaded
    # TODO: Replace with aiida.cmdline.utils.decorators.with_dbenv decocator
    # TODO: when we merge to develop
    if not is_dbenv_loaded():
        load_dbenv()
    from aiida.orm import Group, Node, Computer
    from aiida.orm.querybuilder import QueryBuilder
    from aiida.orm.importexport import export, export_zip

    node_id_set = set(nodes)
    group_dict = dict()

    if group_names:
        qb = QueryBuilder()
        qb.append(Group, tag='group', project=['*'], filters={'name': {'in': group_names}})
        qb.append(Node, tag='node', member_of='group', project=['id'])
        res = qb.dict()

        group_dict.update(
            {group['group']['*'].id: group['group']['*'] for group in res})
        node_id_set.update([node['node']['id'] for node in res])

    if groups:
        qb = QueryBuilder()
        qb.append(Group, tag='group', project=['*'], filters={'id': {'in': groups}})
        qb.append(Node, tag='node', member_of='group', project=['id'])
        res = qb.dict()

        group_dict.update(
            {group['group']['*'].id: group['group']['*'] for group in res})
        node_id_set.update([node['node']['id'] for node in res])

    groups_list = group_dict.values()

    # Getting the nodes that correspond to the ids that were found above
    if len(node_id_set) > 0:
        qb = QueryBuilder()
        qb.append(Node, tag='node', project=['*'], filters={'id': {'in': node_id_set}})
        node_list = [node[0] for node in qb.all()]
    else:
        node_list = list()

    # Check if any of the nodes wasn't found in the database.
    missing_nodes = node_id_set.difference(node.id for node in node_list)
    for node_id in missing_nodes:
        print >> sys.stderr, ('WARNING! Node with pk={} not found, skipping'.format(node_id))

    if computers:
        qb = QueryBuilder()
        qb.append(Computer, tag='comp', project=['*'], filters={'id': {'in': set(computers)}})
        computer_list = [computer[0] for computer in qb.all()]
        missing_computers = set(computers).difference(computer.id for computer in computer_list)

        for computer_id in missing_computers:
            print >> sys.stderr, ('WARNING! Computer with pk={} not found, skipping'.format(computer_id))
    else:
        computer_list = []

    what_list = node_list + computer_list + groups_list
    additional_kwargs = dict()

    if archive_format == 'zip':
        export_function = export_zip
        additional_kwargs.update({'use_compression': True})
    elif archive_format == 'zip-uncompressed':
        export_function = export_zip
        additional_kwargs.update({'use_compression': False})
    elif archive_format == 'tar.gz':
        export_function = export
    else:
        print >> sys.stderr, 'invalid --archive-format value {}'.format(
            archive_format)
        sys.exit(1)

    try:
        export_function(
            what=what_list, input_forward=input_forward,
            create_reversed=create_reversed,
            return_reversed=return_reversed,
            call_reversed=call_reversed, outfile=outfile,
            overwrite=overwrite, **additional_kwargs
        )

    except IOError as e:
        print >> sys.stderr, 'IOError: {}'.format(e.message)
        sys.exit(1)
Ejemplo n.º 14
0
    def get_io_tree(self, nodeId, maxDepth=None):
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm.node import Node

        def addNodes(nodeId, maxDepth, nodes, addedNodes, addedEdges,
                     edgeType):
            qb = QueryBuilder()
            qb.append(Node, tag="main", filters={"id": {"==": nodeId}})
            if edgeType == "ancestors":
                qb.append(Node,
                          tag=edgeType,
                          project=['id', 'type'],
                          edge_project=['path', 'depth'],
                          ancestor_of_beta='main',
                          edge_filters={'depth': {
                              '<=': maxDepth
                          }})
            elif edgeType == "desc":
                qb.append(Node,
                          tag=edgeType,
                          project=['id', 'type'],
                          edge_project=['path', 'depth'],
                          descendant_of_beta='main',
                          edge_filters={'depth': {
                              '<=': maxDepth
                          }})

            if (qb.count() > 0):
                qbResults = qb.get_results_dict()

                for resultDict in qbResults:
                    if resultDict[edgeType]["id"] not in addedNodes:
                        nodes.append({
                            "id":
                            len(addedNodes),
                            "nodeid":
                            resultDict[edgeType]["id"],
                            "nodetype":
                            resultDict[edgeType]["type"],
                            "group":
                            edgeType + "-" +
                            str(resultDict["main--" + edgeType]["depth"])
                        })
                        addedNodes.append(resultDict[edgeType]["id"])

                    path = resultDict["main--" + edgeType]["path"]
                    if edgeType == "ancestors":
                        startEdge = path[0]
                        endEdge = path[1]
                    elif edgeType == "desc":
                        startEdge = path[-2]
                        endEdge = path[-1]
                    if startEdge not in addedEdges.keys():
                        addedEdges[startEdge] = [endEdge]
                    elif endEdge not in addedEdges[startEdge]:
                        addedEdges[startEdge].append(endEdge)

            return nodes, addedNodes, addedEdges

        def addEdges(edges, addedNodes, addedEdges):
            for fromNodeId in addedEdges.keys():
                fromNodeIdIndex = addedNodes.index(fromNodeId)
                for toNodeId in addedEdges[fromNodeId]:
                    toNodeIdIndex = addedNodes.index(toNodeId)
                    edges.append({
                        "from": fromNodeIdIndex,
                        "to": toNodeIdIndex,
                        "arrows": "to",
                        "color": {
                            "inherit": 'from'
                        }
                    })

            return edges

        nodes = []
        edges = []
        addedNodes = []
        addedEdges = {}

        if maxDepth is None:
            from aiida.restapi.common.config import MAX_TREE_DEPTH
            maxDepth = MAX_TREE_DEPTH

        qb = QueryBuilder()
        qb.append(Node,
                  tag="main",
                  project=["id", "type"],
                  filters={"id": {
                      "==": nodeId
                  }})
        if qb.count() > 0:
            mainNode = qb.first()
            nodes.append({
                "id": 0,
                "nodeid": mainNode[0],
                "nodetype": mainNode[1],
                "group": "mainNode"
            })
            addedNodes.append(mainNode[0])

        # get all descendents
        nodes, addedNodes, addedEdges = addNodes(nodeId, maxDepth, nodes,
                                                 addedNodes, addedEdges,
                                                 "ancestors")
        nodes, addedNodes, addedEdges = addNodes(nodeId, maxDepth, nodes,
                                                 addedNodes, addedEdges,
                                                 "desc")

        edges = addEdges(edges, addedNodes, addedEdges)

        return {"nodes": nodes, "edges": edges}
Ejemplo n.º 15
0
def load_node(node_id=None,
              pk=None,
              uuid=None,
              parent_class=None,
              query_with_dashes=True):
    """
    Return an AiiDA node given PK or UUID.

    :param node_id: PK (integer) or UUID (string) or a node
    :param pk: PK of a node
    :param uuid: UUID of a node, or the beginning of the uuid
    :param parent_class: if specified, checks whether the node loaded is a
        subclass of parent_class
    :param bool query_with_dashes: Specific if uuid is passed, allows to put the uuid in the correct form.
        Default=True
    :return: an AiiDA node
    :raise InputValidationError: if none or more than one of parameters is supplied
    :raise TypeError: I the wrong types are provided
    :raise NotExistent: if no matching Node is found.
    :raise MultipleObjectsError: If more than one Node was fouuund
    """
    from aiida.common.exceptions import NotExistent, MultipleObjectsError, NotExistent, InputValidationError
    # This must be done inside here, because at import time the profile
    # must have been already loaded. If you put it at the module level,
    # the implementation is frozen to the default one at import time.
    from aiida.orm.implementation import Node
    from aiida.orm.querybuilder import QueryBuilder

    # First checking if the inputs are valid:
    inputs_provided = [val is not None
                       for val in (node_id, pk, uuid)].count(True)
    if inputs_provided == 0:
        raise InputValidationError(
            "one of the parameters 'node_id', 'pk' and 'uuid' "
            "has to be supplied")
    elif inputs_provided > 1:
        raise InputValidationError(
            "only one of parameters 'node_id', 'pk' and 'uuid' "
            "has to be supplied")

    class_ = parent_class or Node
    if not issubclass(class_, Node):
        raise TypeError("{} is not a subclass of {}".format(class_, Node))
    # The logic is as follows: If pk is specified I will look for the pk
    # if UUID is specified for the uuid.
    # node_id can either be string -> uuid or an integer -> pk
    # Checking first if node_id specified
    if node_id is not None:
        if isinstance(node_id, (str, unicode)):
            uuid = node_id
        elif isinstance(node_id, int):
            pk = node_id
        else:
            raise TypeError("'node_id' has to be either string, unicode or "
                            "integer, {} given".format(type(node_id)))

            #I am checking whether uuid, if supplied,  is a string
    if uuid is not None:
        if not isinstance(uuid, (str, unicode)):
            raise TypeError("'uuid' has to be string or unicode")
        # or whether the pk, if provided, is an integer
    elif pk is not None:
        if not isinstance(pk, int):
            raise TypeError("'pk' has to be an integer")
    else:
        # I really shouldn't get here
        assert True, "Neither pk  nor uuid was provided"

    qb = QueryBuilder()
    qb.append(class_, tag='node', project='*')

    if pk:
        qb.add_filter('node', {'id': pk})
    elif uuid:
        # Put back dashes in the right place
        start_uuid = uuid.replace('-', '')
        if query_with_dashes:
            # Essential that this is ordered from largest to smallest!
            for dash_pos in [20, 16, 12, 8]:
                if len(start_uuid) > dash_pos:
                    start_uuid = "{}-{}".format(start_uuid[:dash_pos],
                                                start_uuid[dash_pos:])

        qb.add_filter('node', {'uuid': {'like': "{}%".format(start_uuid)}})
    try:
        return qb.one()[0]
    except MultipleObjectsError:
        raise MultipleObjectsError("More than one node found")
    except NotExistent:
        raise NotExistent("No node was found")
Ejemplo n.º 16
0
    def process_dummy_data(cls):
        """
        This functions prepare atomic chunks of typical responses from the
        RESTapi and puts them into class attributes

        """
        #TODO: Storing the different nodes as lists and accessing them
        # by their list index is very fragile and a pain to debug.
        # Please change this!
        computer_projections = [
            "id", "uuid", "name", "hostname", "transport_type",
            "scheduler_type"
        ]
        computers = QueryBuilder().append(
            Computer, tag="comp", project=computer_projections).order_by({
                'comp': [{
                    'name': {
                        'order': 'asc'
                    }
                }]
            }).dict()

        # Cast UUID into a string (e.g. in sqlalchemy it comes as a UUID object)
        computers = [_['comp'] for _ in computers]
        for comp in computers:
            if comp['uuid'] is not None:
                comp['uuid'] = str(comp['uuid'])
        cls._dummy_data["computers"] = computers

        calculation_projections = ["id", "uuid", "user_id", "type"]
        calculations = QueryBuilder().append(
            Calculation, tag="calc",
            project=calculation_projections).order_by({
                'calc': [{
                    'id': {
                        'order': 'desc'
                    }
                }]
            }).dict()

        calculations = [_['calc'] for _ in calculations]
        for calc in calculations:
            if calc['uuid'] is not None:
                calc['uuid'] = str(calc['uuid'])
        cls._dummy_data["calculations"] = calculations

        data_projections = ["id", "uuid", "user_id", "type"]
        data_types = {
            'cifdata': CifData,
            'parameterdata': ParameterData,
            'structuredata': StructureData,
            'data': Data,
        }
        for label, dataclass in data_types.iteritems():
            data = QueryBuilder().append(dataclass,
                                         tag="data",
                                         project=data_projections).order_by({
                                             'data': [{
                                                 'id': {
                                                     'order': 'desc'
                                                 }
                                             }]
                                         }).dict()
            data = [_['data'] for _ in data]

            for datum in data:
                if datum['uuid'] is not None:
                    datum['uuid'] = str(datum['uuid'])

            cls._dummy_data[label] = data
Ejemplo n.º 17
0
def upload_psf_family(folder,
                      group_name,
                      group_description,
                      stop_if_existing=True):
    """
    Upload a set of PSF files in a given group.

    :param folder: a path containing all PSF files to be added.
        Only files ending in .PSF (case-insensitive) are considered.
    :param group_name: the name of the group to create. If it exists and is
        non-empty, a UniquenessError is raised.
    :param group_description: a string to be set as the group description.
        Overwrites previous descriptions, if the group was existing.
    :param stop_if_existing: if True, check for the md5 of the files and,
        if the file already exists in the DB, raises a MultipleObjectsError.
        If False, simply adds the existing PsfData node to the group.
    """
    import os
    import aiida.common
    from aiida.common import aiidalogger
    from aiida.orm import Group
    from aiida.common.exceptions import UniquenessError, NotExistent
    from aiida.backends.utils import get_automatic_user
    from aiida.orm.querybuilder import QueryBuilder
    if not os.path.isdir(folder):
        raise ValueError("folder must be a directory")

    # only files, and only those ending with .psf or .PSF;
    # go to the real file if it is a symlink
    files = [
        os.path.realpath(os.path.join(folder, i)) for i in os.listdir(folder)
        if os.path.isfile(os.path.join(folder, i))
        and i.lower().endswith('.psf')
    ]

    nfiles = len(files)

    try:
        group = Group.get(name=group_name, type_string=PSFGROUP_TYPE)
        group_created = False
    except NotExistent:
        group = Group(name=group_name,
                      type_string=PSFGROUP_TYPE,
                      user=get_automatic_user())
        group_created = True

    if group.user != get_automatic_user():
        raise UniquenessError("There is already a PsfFamily group with name {}"
                              ", but it belongs to user {}, therefore you "
                              "cannot modify it".format(
                                  group_name, group.user.email))

    # Always update description, even if the group already existed
    group.description = group_description

    # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY

    pseudo_and_created = []

    for f in files:
        md5sum = aiida.common.utils.md5_file(f)
        qb = QueryBuilder()
        qb.append(PsfData, filters={'attributes.md5': {'==': md5sum}})
        existing_psf = qb.first()

        #existing_psf = PsfData.query(dbattributes__key="md5",
        #                            dbattributes__tval = md5sum)

        if existing_psf is None:
            # return the psfdata instances, not stored
            pseudo, created = PsfData.get_or_create(f,
                                                    use_first=True,
                                                    store_psf=False)
            # to check whether only one psf per element exists
            # NOTE: actually, created has the meaning of "to_be_created"
            pseudo_and_created.append((pseudo, created))
        else:
            if stop_if_existing:
                raise ValueError("A PSF with identical MD5 to "
                                 " {} cannot be added with stop_if_existing"
                                 "".format(f))
            existing_psf = existing_psf[0]
            pseudo_and_created.append((existing_psf, False))

    # check whether pseudo are unique per element
    elements = [(i[0].element, i[0].md5sum) for i in pseudo_and_created]
    # If group already exists, check also that I am not inserting more than
    # once the same element
    if not group_created:
        for aiida_n in group.nodes:
            # Skip non-pseudos
            if not isinstance(aiida_n, PsfData):
                continue
            elements.append((aiida_n.element, aiida_n.md5sum))

    elements = set(elements)  # Discard elements with the same MD5, that would
    # not be stored twice
    elements_names = [e[0] for e in elements]

    if not len(elements_names) == len(set(elements_names)):
        duplicates = set(
            [x for x in elements_names if elements_names.count(x) > 1])
        duplicates_string = ", ".join(i for i in duplicates)
        raise UniquenessError("More than one PSF found for the elements: " +
                              duplicates_string + ".")

    # At this point, save the group, if still unstored
    if group_created:
        group.store()

    # save the psf in the database, and add them to group
    for pseudo, created in pseudo_and_created:
        if created:
            pseudo.store()

            aiidalogger.debug("New node {} created for file {}".format(
                pseudo.uuid, pseudo.filename))
        else:
            aiidalogger.debug("Reusing node {} for file {}".format(
                pseudo.uuid, pseudo.filename))

    # Add elements to the group all togetehr
    group.add_nodes(pseudo for pseudo, created in pseudo_and_created)

    nuploaded = len([_ for _, created in pseudo_and_created if created])

    return nfiles, nuploaded
Ejemplo n.º 18
0
    def test_different_computer_same_name_import(self):
        """
        This test checks that if there is a name collision, the imported
        computers are renamed accordingly.
        """
        import os
        import shutil
        import tempfile

        from aiida.orm.importexport import export
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm.computer import Computer
        from aiida.orm.calculation.job import JobCalculation
        from aiida.orm.importexport import COMP_DUPL_SUFFIX

        # Creating a folder for the import/export files
        export_file_tmp_folder = tempfile.mkdtemp()
        unpack_tmp_folder = tempfile.mkdtemp()

        try:
            # Set the computer name
            comp1_name = "localhost_1"
            self.computer.set_name(comp1_name)

            # Store a calculation
            calc1_label = "calc1"
            calc1 = JobCalculation()
            calc1.set_computer(self.computer)
            calc1.set_resources({
                "num_machines": 1,
                "num_mpiprocs_per_machine": 1
            })
            calc1.label = calc1_label
            calc1.store()
            calc1._set_state(u'RETRIEVING')

            # Export the first job calculation
            filename1 = os.path.join(export_file_tmp_folder, "export1.tar.gz")
            export([calc1.dbnode], outfile=filename1, silent=True)

            # Reset the database
            self.clean_db()
            self.insert_data()

            # Set the computer name to the same name as before
            self.computer.set_name(comp1_name)

            # Store a second calculation
            calc2_label = "calc2"
            calc2 = JobCalculation()
            calc2.set_computer(self.computer)
            calc2.set_resources({
                "num_machines": 2,
                "num_mpiprocs_per_machine": 2
            })
            calc2.label = calc2_label
            calc2.store()
            calc2._set_state(u'RETRIEVING')

            # Export the second job calculation
            filename2 = os.path.join(export_file_tmp_folder, "export2.tar.gz")
            export([calc2.dbnode], outfile=filename2, silent=True)

            # Reset the database
            self.clean_db()
            self.insert_data()

            # Set the computer name to the same name as before
            self.computer.set_name(comp1_name)

            # Store a third calculation
            calc3_label = "calc3"
            calc3 = JobCalculation()
            calc3.set_computer(self.computer)
            calc3.set_resources({
                "num_machines": 2,
                "num_mpiprocs_per_machine": 2
            })
            calc3.label = calc3_label
            calc3.store()
            calc3._set_state(u'RETRIEVING')

            # Export the third job calculation
            filename3 = os.path.join(export_file_tmp_folder, "export3.tar.gz")
            export([calc3.dbnode], outfile=filename3, silent=True)

            # Clean the local database
            self.clean_db()

            # Check that there are no computers
            qb = QueryBuilder()
            qb.append(Computer, project=['*'])
            self.assertEqual(
                qb.count(), 0, "There should not be any computers"
                "in the database at this point.")

            # Check that there are no calculations
            qb = QueryBuilder()
            qb.append(JobCalculation, project=['*'])
            self.assertEqual(
                qb.count(), 0, "There should not be any "
                "calculations in the database at "
                "this point.")

            # Import all the calculations
            import_data(filename1, silent=True)
            import_data(filename2, silent=True)
            import_data(filename3, silent=True)

            # Retrieve the calculation-computer pairs
            qb = QueryBuilder()
            qb.append(JobCalculation, project=['label'], tag='jcalc')
            qb.append(Computer, project=['name'], computer_of='jcalc')
            self.assertEqual(qb.count(), 3, "Three combinations expected.")
            res = qb.all()
            self.assertIn([calc1_label, comp1_name], res,
                          "Calc-Computer combination not found.")
            self.assertIn(
                [calc2_label, comp1_name + COMP_DUPL_SUFFIX.format(0)], res,
                "Calc-Computer combination not found.")
            self.assertIn(
                [calc3_label, comp1_name + COMP_DUPL_SUFFIX.format(1)], res,
                "Calc-Computer combination not found.")
        finally:
            # Deleting the created temporary folders
            shutil.rmtree(export_file_tmp_folder, ignore_errors=True)
            shutil.rmtree(unpack_tmp_folder, ignore_errors=True)
Ejemplo n.º 19
0
    def test_same_computer_different_name_import(self):
        """
        This test checks that if the computer is re-imported with a different
        name to the same database, then the original computer will not be
        renamed. It also checks that the names were correctly imported (without
        any change since there is no computer name collision)
        """
        import os
        import shutil
        import tempfile

        from aiida.orm.importexport import export
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm.computer import Computer
        from aiida.orm.calculation.job import JobCalculation

        # Creating a folder for the import/export files
        export_file_tmp_folder = tempfile.mkdtemp()
        unpack_tmp_folder = tempfile.mkdtemp()

        try:
            # Store a calculation
            calc1_label = "calc1"
            calc1 = JobCalculation()
            calc1.set_computer(self.computer)
            calc1.set_resources({
                "num_machines": 1,
                "num_mpiprocs_per_machine": 1
            })
            calc1.label = calc1_label
            calc1.store()
            calc1._set_state(u'RETRIEVING')

            # Store locally the computer name
            comp1_name = unicode(self.computer.name)

            # Export the first job calculation
            filename1 = os.path.join(export_file_tmp_folder, "export1.tar.gz")
            export([calc1.dbnode], outfile=filename1, silent=True)

            # Rename the computer
            self.computer.set_name(comp1_name + "_updated")

            # Store a second calculation
            calc2_label = "calc2"
            calc2 = JobCalculation()
            calc2.set_computer(self.computer)
            calc2.set_resources({
                "num_machines": 2,
                "num_mpiprocs_per_machine": 2
            })
            calc2.label = calc2_label
            calc2.store()
            calc2._set_state(u'RETRIEVING')

            # Export the second job calculation
            filename2 = os.path.join(export_file_tmp_folder, "export2.tar.gz")
            export([calc2.dbnode], outfile=filename2, silent=True)

            # Clean the local database
            self.clean_db()

            # Check that there are no computers
            qb = QueryBuilder()
            qb.append(Computer, project=['*'])
            self.assertEqual(
                qb.count(), 0, "There should not be any computers"
                "in the database at this point.")

            # Check that there are no calculations
            qb = QueryBuilder()
            qb.append(JobCalculation, project=['*'])
            self.assertEqual(
                qb.count(), 0, "There should not be any "
                "calculations in the database at "
                "this point.")

            # Import the first calculation
            import_data(filename1, silent=True)

            # Check that the calculation computer is imported correctly.
            qb = QueryBuilder()
            qb.append(JobCalculation, project=['label'])
            self.assertEqual(qb.count(), 1, "Only one calculation should be "
                             "found.")
            self.assertEqual(unicode(qb.first()[0]), calc1_label,
                             "The calculation label is not correct.")

            # Check that the referenced computer is imported correctly.
            qb = QueryBuilder()
            qb.append(Computer, project=['name', 'uuid', 'id'])
            self.assertEqual(qb.count(), 1, "Only one computer should be "
                             "found.")
            self.assertEqual(unicode(qb.first()[0]), comp1_name,
                             "The computer name is not correct.")

            # Import the second calculation
            import_data(filename2, silent=True)

            # Check that the number of computers remains the same and its data
            # did not change.
            qb = QueryBuilder()
            qb.append(Computer, project=['name'])
            self.assertEqual(qb.count(), 1, "Only one computer should be "
                             "found.")
            self.assertEqual(unicode(qb.first()[0]), comp1_name,
                             "The computer name is not correct.")

        finally:
            # Deleting the created temporary folders
            shutil.rmtree(export_file_tmp_folder, ignore_errors=True)
            shutil.rmtree(unpack_tmp_folder, ignore_errors=True)
Ejemplo n.º 20
0
    def _add_dblink_from(self,
                         src,
                         label=None,
                         link_type=LinkType.UNSPECIFIED):
        from aiida.backends.sqlalchemy import get_scoped_session
        from aiida.orm.querybuilder import QueryBuilder
        session = get_scoped_session()
        if not isinstance(src, Node):
            raise ValueError("src must be a Node instance")
        if self.uuid == src.uuid:
            raise ValueError("Cannot link to itself")

        if self._to_be_stored:
            raise ModificationNotAllowed(
                "Cannot call the internal _add_dblink_from if the "
                "destination node is not stored")
        if src._to_be_stored:
            raise ModificationNotAllowed(
                "Cannot call the internal _add_dblink_from if the "
                "source node is not stored")

        # Check for cycles. This works if the transitive closure is enabled; if
        # it isn't, this test will never fail, but then having a circular link
        # is not meaningful but does not pose a huge threat
        #
        # I am linking src->self; a loop would be created if a DbPath exists
        # already in the TC table from self to src
        if link_type is LinkType.CREATE or link_type is LinkType.INPUT:
            if QueryBuilder().append(
                    Node, filters={
                        'id': self.pk
                    }, tag='parent').append(
                        Node,
                        filters={
                            'id': src.pk
                        },
                        tag='child',
                        descendant_of='parent').count() > 0:
                raise ValueError(
                    "The link you are attempting to create would generate a loop"
                )

        if label is None:
            autolabel_idx = 1

            existing_from_autolabels = session.query(DbLink.label).filter(
                DbLink.output_id == self.dbnode.id, DbLink.label.like("link%"))

            while "link_{}".format(autolabel_idx) in existing_from_autolabels:
                autolabel_idx += 1

            safety_counter = 0
            while True:
                safety_counter += 1
                if safety_counter > 100:
                    # Well, if you have more than 100 concurrent addings
                    # to the same node, you are clearly doing something wrong...
                    raise InternalError(
                        "Hey! We found more than 100 concurrent"
                        " adds of links "
                        "to the same nodes! Are you really doing that??")
                try:
                    self._do_create_link(src, "link_{}".format(autolabel_idx),
                                         link_type)
                    break
                except UniquenessError:
                    # Retry loop until you find a new loop
                    autolabel_idx += 1
        else:
            self._do_create_link(src, label, link_type)
Ejemplo n.º 21
0
    def run(self, *args):
        load_dbenv()

        import argparse

        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm import Group, Node, Computer
        from aiida.orm.importexport import export, export_zip

        parser = argparse.ArgumentParser(
            prog=self.get_full_command_name(),
            description='Export data from the DB.')
        parser.add_argument('-c',
                            '--computers',
                            nargs='+',
                            type=int,
                            metavar="PK",
                            help="Export the given computers")
        parser.add_argument('-n',
                            '--nodes',
                            nargs='+',
                            type=int,
                            metavar="PK",
                            help="Export the given nodes")
        parser.add_argument('-g',
                            '--groups',
                            nargs='+',
                            metavar="GROUPNAME",
                            help="Export all nodes in the given group(s), "
                            "identified by name.",
                            type=str)
        parser.add_argument('-G',
                            '--group_pks',
                            nargs='+',
                            metavar="PK",
                            help="Export all nodes in the given group(s), "
                            "identified by pk.",
                            type=str)
        parser.add_argument('-P',
                            '--no-parents',
                            dest='no_parents',
                            action='store_true',
                            help="Store only the nodes that are explicitly "
                            "given, without exporting the parents")
        parser.set_defaults(no_parents=False)
        parser.add_argument('-O',
                            '--no-calc-outputs',
                            dest='no_calc_outputs',
                            action='store_true',
                            help="If a calculation is included in the list of "
                            "nodes to export, do not export its outputs")
        parser.set_defaults(no_calc_outputs=False)
        parser.add_argument('-y',
                            '--overwrite',
                            dest='overwrite',
                            action='store_true',
                            help="Overwrite the output file, if it exists")
        parser.set_defaults(overwrite=False)

        zipsubgroup = parser.add_mutually_exclusive_group()
        zipsubgroup.add_argument(
            '-z',
            '--zipfile-compressed',
            dest='zipfilec',
            action='store_true',
            help="Store as zip file (experimental, should be "
            "faster")
        zipsubgroup.add_argument('-Z',
                                 '--zipfile-uncompressed',
                                 dest='zipfileu',
                                 action='store_true',
                                 help="Store as uncompressed zip file "
                                 "(experimental, should be faster")
        parser.set_defaults(zipfilec=False)
        parser.set_defaults(zipfileu=False)

        parser.add_argument('output_file',
                            type=str,
                            help='The output file name for the export file')

        parsed_args = parser.parse_args(args)

        if parsed_args.nodes is None:
            node_id_set = set()
        else:
            node_id_set = set(parsed_args.nodes)

        group_dict = dict()

        if parsed_args.groups is not None:
            qb = QueryBuilder()
            qb.append(Group,
                      tag='group',
                      project=['*'],
                      filters={'name': {
                          'in': parsed_args.groups
                      }})
            qb.append(Node, tag='node', member_of='group', project=['id'])
            res = qb.dict()

            group_dict.update(
                {_['group']['*'].name: _['group']['*'].dbgroup
                 for _ in res})
            node_id_set.update([_['node']['id'] for _ in res])

        if parsed_args.group_pks is not None:
            qb = QueryBuilder()
            qb.append(Group,
                      tag='group',
                      project=['*'],
                      filters={'id': {
                          'in': parsed_args.group_pks
                      }})
            qb.append(Node, tag='node', member_of='group', project=['id'])
            res = qb.dict()

            group_dict.update(
                {_['group']['*'].name: _['group']['*'].dbgroup
                 for _ in res})
            node_id_set.update([_['node']['id'] for _ in res])

        # The db_groups that correspond to what was searched above
        dbgroups_list = group_dict.values()

        # Getting the nodes that correspond to the ids that were found above
        if len(node_id_set) > 0:
            qb = QueryBuilder()
            qb.append(Node,
                      tag='node',
                      project=['*'],
                      filters={'id': {
                          'in': node_id_set
                      }})
            node_list = [_[0] for _ in qb.all()]
        else:
            node_list = list()

        # Check if any of the nodes wasn't found in the database.
        missing_nodes = node_id_set.difference(_.id for _ in node_list)
        for id in missing_nodes:
            print >> sys.stderr, ("WARNING! Node with pk= {} "
                                  "not found, skipping.".format(id))

        # The dbnodes of the above node list
        dbnode_list = [_.dbnode for _ in node_list]

        if parsed_args.computers is not None:
            qb = QueryBuilder()
            qb.append(Computer,
                      tag='comp',
                      project=['*'],
                      filters={'id': {
                          'in': set(parsed_args.computers)
                      }})
            computer_list = [_[0] for _ in qb.all()]
            missing_computers = set(parsed_args.computers).difference(
                _.id for _ in computer_list)
            for id in missing_computers:
                print >> sys.stderr, ("WARNING! Computer with pk= {} "
                                      "not found, skipping.".format(id))
        else:
            computer_list = []

        # The dbcomputers of the above computer list
        dbcomputer_list = [_.dbcomputer for _ in computer_list]

        what_list = dbnode_list + dbcomputer_list + dbgroups_list

        export_function = export
        additional_kwargs = {}
        if parsed_args.zipfileu:
            export_function = export_zip
            additional_kwargs.update({"use_compression": False})
        elif parsed_args.zipfilec:
            export_function = export_zip
            additional_kwargs.update({"use_compression": True})
        try:
            export_function(what=what_list,
                            also_parents=not parsed_args.no_parents,
                            also_calc_outputs=not parsed_args.no_calc_outputs,
                            outfile=parsed_args.output_file,
                            overwrite=parsed_args.overwrite,
                            **additional_kwargs)
        except IOError as e:
            print >> sys.stderr, "IOError: {}".format(e.message)
            sys.exit(1)
Ejemplo n.º 22
0
    def get_bands_and_parents_structure(self, args):
        """
        Search for bands and return bands and the closest structure that is a parent of the instance.
        This is the backend independent way, can be overriden for performance reason

        :returns:
            A list of sublists, each latter containing (in order):
                pk as string, formula as string, creation date, bandsdata-label
        """

        import datetime
        from aiida.utils import timezone
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.backends.utils import get_automatic_user
        from aiida.orm.implementation import User
        from aiida.orm.implementation import Group
        from aiida.orm.data.structure import (get_formula, get_symbols_string)
        from aiida.orm.data.array.bands import BandsData
        from aiida.orm.data.structure import StructureData

        qb = QueryBuilder()
        if args.all_users is False:
            au = get_automatic_user()
            user = User(dbuser=au)
            qb.append(User, tag="creator", filters={"email": user.email})
        else:
            qb.append(User, tag="creator")

        bdata_filters = {}
        if args.past_days is not None:
            now = timezone.now()
            n_days_ago = now - datetime.timedelta(days=args.past_days)
            bdata_filters.update({"ctime": {'>=': n_days_ago}})

        qb.append(BandsData,
                  tag="bdata",
                  created_by="creator",
                  filters=bdata_filters,
                  project=["id", "label", "ctime"])

        group_filters = {}

        if args.group_name is not None:
            group_filters.update({"name": {"in": args.group_name}})
        if args.group_pk is not None:
            group_filters.update({"id": {"in": args.group_pk}})
        if group_filters:
            qb.append(Group,
                      tag="group",
                      filters=group_filters,
                      group_of="bdata")

        qb.append(
            StructureData,
            tag="sdata",
            ancestor_of="bdata",
            # We don't care about the creator of StructureData
            project=["id", "attributes.kinds", "attributes.sites"])

        qb.order_by({StructureData: {'ctime': 'desc'}})

        list_data = qb.distinct()

        entry_list = []
        already_visited_bdata = set()

        for [bid, blabel, bdate, sid, akinds, asites] in list_data.all():

            # We process only one StructureData per BandsData.
            # We want to process the closest StructureData to
            # every BandsData.
            # We hope that the StructureData with the latest
            # creation time is the closest one.
            # This will be updated when the QueryBuilder supports
            # order_by by the distance of two nodes.
            if already_visited_bdata.__contains__(bid):
                continue
            already_visited_bdata.add(bid)

            if args.element is not None:
                all_symbols = [_["symbols"][0] for _ in akinds]
                if not any([s in args.element for s in all_symbols]):
                    continue

            if args.element_only is not None:
                all_symbols = [_["symbols"][0] for _ in akinds]
                if not all([s in all_symbols for s in args.element_only]):
                    continue

            # We want only the StructureData that have attributes
            if akinds is None or asites is None:
                continue

            symbol_dict = {}
            for k in akinds:
                symbols = k['symbols']
                weights = k['weights']
                symbol_dict[k['name']] = get_symbols_string(symbols, weights)

            try:
                symbol_list = []
                for s in asites:
                    symbol_list.append(symbol_dict[s['kind_name']])
                formula = get_formula(symbol_list, mode=args.formulamode)
            # If for some reason there is no kind with the name
            # referenced by the site
            except KeyError:
                formula = "<<UNKNOWN>>"
            entry_list.append(
                [str(bid),
                 str(formula),
                 bdate.strftime('%d %b %Y'), blabel])

        return entry_list
Ejemplo n.º 23
0
    def code_list(self, *args):
        """
        List available codes
        """
        import argparse

        parser = argparse.ArgumentParser(
            prog=self.get_full_command_name(),
            description='List the codes in the database.')
        # The default states are those that are shown if no option is given
        parser.add_argument(
            '-c',
            '--computer',
            help="Filter only codes on a given computer",
        )
        parser.add_argument(
            '-p',
            '--plugin',
            help="Filter only calculation with a given plugin",
        )
        parser.add_argument(
            '-A',
            '--all-users',
            dest='all_users',
            action='store_true',
            help="Show codes of all users",
        )
        parser.add_argument(
            '-o',
            '--show-owner',
            dest='show_owner',
            action='store_true',
            help="Show also the owner of the code",
        )
        parser.add_argument(
            '-a',
            '--all-codes',
            action='store_true',
            help="Show also hidden codes",
        )
        parser.set_defaults(all_users=False, hidden=False)
        parsed_args = parser.parse_args(args)
        computer_filter = parsed_args.computer
        plugin_filter = parsed_args.plugin
        all_users = parsed_args.all_users
        show_owner = parsed_args.show_owner
        reveal_filter = parsed_args.all_codes

        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm.code import Code
        from aiida.orm.computer import Computer
        from aiida.orm.user import User
        from aiida.backends.utils import get_automatic_user

        qb_user_filters = dict()
        if not all_users:
            user = User(dbuser=get_automatic_user())
            qb_user_filters['email'] = user.email

        qb_computer_filters = dict()
        if computer_filter is not None:
            qb_computer_filters['name'] = computer_filter

        qb_code_filters = dict()
        if plugin_filter is not None:
            qb_code_filters['attributes.input_plugin'] = plugin_filter

        if not reveal_filter:
            qb_code_filters['attributes.hidden'] = {"~==": True}

        print "# List of configured codes:"
        print "# (use 'verdi code show CODEID' to see the details)"
        if computer_filter is not None:
            qb = QueryBuilder()
            qb.append(Code,
                      tag="code",
                      filters=qb_code_filters,
                      project=["id", "label"])
            # We have a user assigned to the code so we can ask for the
            # presence of a user even if there is no user filter
            qb.append(User,
                      creator_of="code",
                      project=["email"],
                      filters=qb_user_filters)
            # We also add the filter on computer. This will automatically
            # return codes that have a computer (and of course satisfy the
            # other filters). The codes that have a computer attached are the
            # remote codes.
            qb.append(Computer,
                      computer_of="code",
                      project=["name"],
                      filters=qb_computer_filters)
            self.print_list_res(qb, show_owner)

        # If there is no filter on computers
        else:
            # Print all codes that have a computer assigned to them
            # (these are the remote codes)
            qb = QueryBuilder()
            qb.append(Code,
                      tag="code",
                      filters=qb_code_filters,
                      project=["id", "label"])
            # We have a user assigned to the code so we can ask for the
            # presence of a user even if there is no user filter
            qb.append(User,
                      creator_of="code",
                      project=["email"],
                      filters=qb_user_filters)
            qb.append(Computer, computer_of="code", project=["name"])
            self.print_list_res(qb, show_owner)

            # Now print all the local codes. To get the local codes we ask
            # the dbcomputer_id variable to be None.
            qb = QueryBuilder()
            comp_non_existence = {"dbcomputer_id": {"==": None}}
            if not qb_code_filters:
                qb_code_filters = comp_non_existence
            else:
                new_qb_code_filters = {
                    "and": [qb_code_filters, comp_non_existence]
                }
                qb_code_filters = new_qb_code_filters
            qb.append(Code,
                      tag="code",
                      filters=qb_code_filters,
                      project=["id", "label"])
            # We have a user assigned to the code so we can ask for the
            # presence of a user even if there is no user filter
            qb.append(User,
                      creator_of="code",
                      project=["email"],
                      filters=qb_user_filters)
            self.print_list_res(qb, show_owner)
Ejemplo n.º 24
0
    def computer_configure(self, *args):
        """
        Configure the authentication information for a given computer
        """
        if not is_dbenv_loaded():
            load_dbenv()

        import readline
        import inspect

        from django.core.exceptions import ObjectDoesNotExist

        from aiida.common.exceptions import (NotExistent, ValidationError)
        from aiida.backends.utils import get_automatic_user
        from aiida.common.utils import get_configured_user_email
        from aiida.backends.settings import BACKEND
        from aiida.backends.profile import BACKEND_SQLA, BACKEND_DJANGO

        import argparse

        parser = argparse.ArgumentParser(
            prog=self.get_full_command_name(),
            description='Configure a computer for a given AiiDA user.')
        # The default states are those that are shown if no option is given
        parser.add_argument(
            '-u',
            '--user',
            type=str,
            metavar='EMAIL',
            help=
            "Configure the computer for the given AiiDA user (otherwise, configure the current default user)",
        )
        parser.add_argument(
            'computer',
            type=str,
            help="The name of the computer that you want to configure")

        parsed_args = parser.parse_args(args)

        user_email = parsed_args.user
        computername = parsed_args.computer

        try:
            computer = self.get_computer(name=computername)
        except NotExistent:
            print >> sys.stderr, "No computer exists with name '{}'".format(
                computername)
            sys.exit(1)
        if user_email is None:
            user = get_automatic_user()
        else:
            from aiida.orm.querybuilder import QueryBuilder
            qb = QueryBuilder()
            qb.append(type="user", filters={'email': user_email})
            user = qb.first()
            if user is None:
                print >> sys.stderr, ("No user with email '{}' in the "
                                      "database.".format(user_email))
                sys.exit(1)

        if BACKEND == BACKEND_DJANGO:
            from aiida.backends.djsite.db.models import DbAuthInfo

            try:
                authinfo = DbAuthInfo.objects.get(
                    dbcomputer=computer.dbcomputer, aiidauser=user)

                old_authparams = authinfo.get_auth_params()
            except ObjectDoesNotExist:
                authinfo = DbAuthInfo(dbcomputer=computer.dbcomputer,
                                      aiidauser=user)
                old_authparams = {}

        elif BACKEND == BACKEND_SQLA:
            from aiida.backends.sqlalchemy.models.authinfo import DbAuthInfo
            from aiida.backends.sqlalchemy import session

            authinfo = session.query(DbAuthInfo).filter(
                DbAuthInfo.dbcomputer == computer.dbcomputer).filter(
                    DbAuthInfo.aiidauser == user).first()
            if authinfo is None:
                authinfo = DbAuthInfo(dbcomputer=computer.dbcomputer,
                                      aiidauser=user)
                old_authparams = {}
            else:
                old_authparams = authinfo.get_auth_params()
        else:
            raise Exception("Unknown backend {}".format(BACKEND))
        Transport = computer.get_transport_class()

        print("Configuring computer '{}' for the AiiDA user '{}'".format(
            computername, user.email))

        print "Computer {} has transport of type {}".format(
            computername, computer.get_transport_type())

        if user.email != get_configured_user_email():
            print "*" * 72
            print "** {:66s} **".format("WARNING!")
            print "** {:66s} **".format(
                "  You are configuring a different user.")
            print "** {:66s} **".format(
                "  Note that the default suggestions are taken from your")
            print "** {:66s} **".format(
                "  local configuration files, so they may be incorrect.")
            print "*" * 72

        valid_keys = Transport.get_valid_auth_params()

        default_authparams = {}
        for k in valid_keys:
            if k in old_authparams:
                default_authparams[k] = old_authparams.pop(k)
        if old_authparams:
            print(
                "WARNING: the following keys were previously in the "
                "authorization parameters,")
            print "but have not been recognized and have been deleted:"
            print ", ".join(old_authparams.keys())

        if not valid_keys:
            print "There are no special keys to be configured. Configuration completed."
            authinfo.set_auth_params({})
            authinfo.save()
            return

        print ""
        print "Note: to leave a field unconfigured, leave it empty and press [Enter]"

        # I strip out the old auth_params that are not among the valid keys

        new_authparams = {}

        for k in valid_keys:
            key_set = False
            while not key_set:
                try:
                    converter_name = '_convert_{}_fromstring'.format(k)
                    try:
                        converter = dict(
                            inspect.getmembers(Transport))[converter_name]
                    except KeyError:
                        print >> sys.stderr, (
                            "Internal error! "
                            "No {} defined in Transport {}".format(
                                converter_name, computer.get_transport_type()))
                        sys.exit(1)

                    if k in default_authparams:
                        readline.set_startup_hook(lambda: readline.insert_text(
                            str(default_authparams[k])))
                    else:
                        # Use suggestion only if parameters were not already set
                        suggester_name = '_get_{}_suggestion_string'.format(k)
                        try:
                            suggester = dict(
                                inspect.getmembers(Transport))[suggester_name]
                            suggestion = suggester(computer)
                            readline.set_startup_hook(
                                lambda: readline.insert_text(suggestion))
                        except KeyError:
                            readline.set_startup_hook()

                    txtval = raw_input("=> {} = ".format(k))
                    if txtval:
                        new_authparams[k] = converter(txtval)
                    key_set = True
                except ValidationError as e:
                    print "Error in the inserted value: {}".format(e.message)

        authinfo.set_auth_params(new_authparams)
        authinfo.save()
        print "Configuration stored for your user on computer '{}'.".format(
            computername)
Ejemplo n.º 25
0
    def test_creation_and_deletion(self):
        from aiida.backends.djsite.db.models import DbLink  # Direct links
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.common.links import LinkType

        n1 = Node().store()
        n2 = Node().store()
        n3 = Node().store()
        n4 = Node().store()
        n5 = Node().store()
        n6 = Node().store()
        n7 = Node().store()
        n8 = Node().store()
        n9 = Node().store()

        # I create a strange graph, inserting links in a order
        # such that I often have to create the transitive closure
        # between two graphs
        n3.add_link_from(n2, link_type=LinkType.CREATE)
        n2.add_link_from(n1, link_type=LinkType.CREATE)
        n5.add_link_from(n3, link_type=LinkType.CREATE)
        n5.add_link_from(n4, link_type=LinkType.CREATE)
        n4.add_link_from(n2, link_type=LinkType.CREATE)

        n7.add_link_from(n6, link_type=LinkType.CREATE)
        n8.add_link_from(n7, link_type=LinkType.CREATE)

        # Yet, no links from 1 to 8
        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 0)

        n6.add_link_from(n5, link_type=LinkType.INPUT)

        # Yet, now 2 links from 1 to 8
        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 2)

        n7.add_link_from(n9, link_type=LinkType.INPUT)
        # Still two links...
        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 2)

        n9.add_link_from(n6, link_type=LinkType.INPUT)
        # And now there should be 4 nodes
        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 4)

        ### I start deleting now

        # I cut one branch below: I should loose 2 links
        DbLink.objects.filter(input=n6, output=n9).delete()

        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 2)

        DbLink.objects.filter(input=n2, output=n4).delete()

        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 1)
        #~ self.assertEquals(
        #~ len(DbPath.objects.filter(parent=n1, child=n8).distinct()), 1)

        # Another cut should delete all links
        DbLink.objects.filter(input=n3, output=n5).delete()
        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 0)
        #~ self.assertEquals(
        #~ len(DbPath.objects.filter(parent=n1, child=n8).distinct()), 0)

        # But I did not delete everything! For instance, I can check
        # the following links
        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n4.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 1)
        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n5.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n7.pk
                                 }).count(), 1)
        #~ self.assertEquals(
        #~ len(DbPath.objects.filter(parent=n4, child=n8).distinct()), 1)
        #~ self.assertEquals(
        #~ len(DbPath.objects.filter(parent=n5, child=n7).distinct()), 1)

        # Finally, I reconnect in a different way the two graphs and
        # check that 1 and 8 are again connected
        n4.add_link_from(n3, link_type=LinkType.INPUT)

        self.assertEquals(
            QueryBuilder().append(Node, filters={
                'id': n1.pk
            }, tag='anc').append(Node,
                                 descendant_of='anc',
                                 filters={
                                     'id': n8.pk
                                 }).count(), 1)
Ejemplo n.º 26
0
    def calculation_cleanworkdir(self, *args):
        """
        Clean all the content of all the output remote folders of calculations,
        passed as a list of pks, or identified by modification time.

        If a list of calculation PKs is not passed through -c option, one of
        the option -p or -u has to be specified (if both are given, a logical
        AND is done between the 2 - you clean out calculations modified AFTER
        [-p option] days from now but BEFORE [-o option] days from now).
        If you also pass the -f option, no confirmation will be asked.
        """
        import argparse

        parser = argparse.ArgumentParser(
            prog=self.get_full_command_name(),
            description="Clean work directory (i.e. remote folder) of AiiDA "
            "calculations.")
        parser.add_argument("-k",
                            "--pk",
                            metavar="PK",
                            type=int,
                            nargs="+",
                            help="The principal key (PK) of the calculations "
                            "to clean the workdir of",
                            dest="pk")
        parser.add_argument("-f",
                            "--force",
                            action="store_true",
                            help="Force the cleaning (no prompt)")
        parser.add_argument("-p",
                            "--past-days",
                            metavar="N",
                            help="Add a filter to clean workdir of "
                            "calculations modified during the past N "
                            "days",
                            type=int,
                            action="store",
                            dest="past_days")
        parser.add_argument("-o",
                            "--older-than",
                            metavar="N",
                            help="Add a filter to clean workdir of "
                            "calculations that have been modified on a "
                            "date before N days ago",
                            type=int,
                            action="store",
                            dest="older_than")
        parser.add_argument("-c",
                            "--computers",
                            metavar="label",
                            nargs="+",
                            help="Add a filter to clean workdir of "
                            "calculations on this computer(s) only",
                            type=str,
                            action="store",
                            dest="computer")

        if not is_dbenv_loaded():
            load_dbenv()

        from aiida.backends.utils import get_automatic_user
        from aiida.backends.utils import get_authinfo
        from aiida.common.utils import query_yes_no
        from aiida.orm.computer import Computer as OrmComputer
        from aiida.orm.user import User as OrmUser
        from aiida.orm.calculation import Calculation as OrmCalculation
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.utils import timezone
        import datetime

        parsed_args = parser.parse_args(args)

        # If a pk is given then the -o & -p options should not be specified
        if parsed_args.pk is not None:
            if ((parsed_args.past_days is not None)
                    or (parsed_args.older_than is not None)):
                print(
                    "You cannot specify both a list of calculation pks and "
                    "the -p or -o options")
                return
        # If no pk is given then at least one of the -o & -p options should be
        # specified
        else:
            if ((parsed_args.past_days is None)
                    and (parsed_args.older_than is None)):
                print(
                    "You should specify at least a list of calculations or "
                    "the -p, -o options")
                return

        # At this point we know that either the pk or the -p -o options are
        # specified

        # We also check that not both -o & -p options are specified
        if ((parsed_args.past_days is not None)
                and (parsed_args.older_than is not None)):
            print(
                "Not both of the -p, -o options can be specified in the "
                "same time")
            return

        qb_user_filters = dict()
        user = OrmUser(dbuser=get_automatic_user())
        qb_user_filters["email"] = user.email

        qb_computer_filters = dict()
        if parsed_args.computer is not None:
            qb_computer_filters["name"] = {"in": parsed_args.computer}

        qb_calc_filters = dict()
        if parsed_args.past_days is not None:
            pd_ts = timezone.now() - datetime.timedelta(
                days=parsed_args.past_days)
            qb_calc_filters["mtime"] = {">": pd_ts}
        if parsed_args.older_than is not None:
            ot_ts = timezone.now() - datetime.timedelta(
                days=parsed_args.older_than)
            qb_calc_filters["mtime"] = {"<": ot_ts}
        if parsed_args.pk is not None:
            print("parsed_args.pk: ", parsed_args.pk)
            qb_calc_filters["id"] = {"in": parsed_args.pk}

        qb = QueryBuilder()
        qb.append(OrmCalculation,
                  tag="calc",
                  filters=qb_calc_filters,
                  project=["id", "uuid", "attributes.remote_workdir"])
        qb.append(OrmComputer,
                  computer_of="calc",
                  project=["*"],
                  filters=qb_computer_filters)
        qb.append(OrmUser,
                  creator_of="calc",
                  project=["*"],
                  filters=qb_user_filters)

        no_of_calcs = qb.count()
        if no_of_calcs == 0:
            print("No calculations found with the given criteria.")
            return

        print("Found {} calculations with the given criteria.".format(
            no_of_calcs))

        if not parsed_args.force:
            if not query_yes_no(
                    "Are you sure you want to clean the work "
                    "directory?", "no"):
                return

        # get the uuids of all calculations matching the filters
        calc_list_data = qb.dict()

        # get all computers associated to the calc uuids above, and load them
        # we group them by uuid to avoid computer duplicates
        comp_uuid_to_computers = {
            _["computer"]["*"].uuid: _["computer"]["*"]
            for _ in calc_list_data
        }

        # now build a dictionary with the info of folders to delete
        remotes = {}
        for computer in comp_uuid_to_computers.values():
            # initialize a key of info for a given computer
            remotes[computer.name] = {
                'transport':
                get_authinfo(computer=computer,
                             aiidauser=user._dbuser).get_transport(),
                'computer':
                computer,
            }

            # select the calc pks done on this computer
            this_calc_pks = [
                _["calc"]["id"] for _ in calc_list_data
                if _["computer"]["*"].id == computer.id
            ]

            this_calc_uuids = [
                unicode(_["calc"]["uuid"]) for _ in calc_list_data
                if _["computer"]["*"].id == computer.id
            ]

            remote_workdirs = [
                _["calc"]["attributes.remote_workdir"] for _ in calc_list_data
                if _["calc"]["id"] in this_calc_pks
                if _["calc"]["attributes.remote_workdir"] is not None
            ]

            remotes[computer.name]['remotes'] = remote_workdirs
            remotes[computer.name]['uuids'] = this_calc_uuids

        # now proceed to cleaning
        for computer, dic in remotes.iteritems():
            print(
                "Cleaning the work directory on computer {}.".format(computer))
            counter = 0
            t = dic['transport']
            with t:
                remote_user = remote_user = t.whoami()
                aiida_workdir = dic['computer'].get_workdir().format(
                    username=remote_user)

                t.chdir(aiida_workdir)
                # Hardcoding the sharding equal to 3 parts!
                existing_folders = t.glob('*/*/*')

                folders_to_delete = [
                    i for i in existing_folders
                    if i.replace("/", "") in dic['uuids']
                ]

                for folder in folders_to_delete:
                    t.rmtree(folder)
                    counter += 1
                    if counter % 20 == 0 and counter > 0:
                        print("Deleted work directories: {}".format(counter))

            print("{} remote folder(s) cleaned.".format(counter))
Ejemplo n.º 27
0
    def query_jobcalculations_by_computer_user_state(
            self,
            state,
            computer=None,
            user=None,
            only_computer_user_pairs=False,
            only_enabled=True,
            limit=None):
        """
        Filter all calculations with a given state.

        Issue a warning if the state is not in the list of valid states.

        :param string state: The state to be used to filter (should be a string among
                those defined in aiida.common.datastructures.calc_states)
        :param computer: a Django DbComputer entry, or a Computer object, of a
                computer in the DbComputer table.
                A string for the hostname is also valid.
        :param user: a Django entry (or its pk) of a user in the DbUser table;
                if present, the results are restricted to calculations of that
                specific user
        :param bool only_computer_user_pairs: if False (default) return a queryset
                where each element is a suitable instance of Node (it should
                be an instance of Calculation, if everything goes right!)
                If True, return only a list of tuples, where each tuple is
                in the format
                ('dbcomputer__id', 'user__id')
                [where the IDs are the IDs of the respective tables]
        :param int limit: Limit the number of rows returned

        :return: a list of calculation objects matching the filters.
        """
        # I assume that calc_states are strings. If this changes in the future,
        # update the filter below from dbattributes__tval to the correct field.
        from aiida.orm.computer import Computer
        from aiida.orm.calculation.job import JobCalculation
        from aiida.orm.user import User
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.common.exceptions import InputValidationError
        from aiida.common.datastructures import calc_states

        if state not in calc_states:
            raise InputValidationError(
                "querying for calculation state='{}', but it "
                "is not a valid calculation state".format(state))

        calcfilter = {'state': {'==': state}}
        computerfilter = {"enabled": {'==': True}}
        userfilter = {}

        if computer is None:
            pass
        elif isinstance(computer, int):
            computerfilter.update({'id': {'==': computer}})
        elif isinstance(computer, Computer):
            computerfilter.update({'id': {'==': computer.pk}})
        else:
            try:
                computerfilter.update({'id': {'==': computer.id}})
            except AttributeError as e:
                raise Exception("{} is not a valid computer\n{}".format(
                    computer, e))
        if user is None:
            pass
        elif isinstance(user, int):
            userfilter.update({'id': {'==': user}})
        else:
            try:
                userfilter.update({'id': {'==': int(user.id)}})
                # Is that safe?
            except:
                raise Exception("{} is not a valid user".format(user))

        qb = QueryBuilder()
        qb.append(type="computer", tag='computer', filters=computerfilter)
        qb.append(JobCalculation,
                  filters=calcfilter,
                  tag='calc',
                  has_computer='computer')
        qb.append(type="user",
                  tag='user',
                  filters=userfilter,
                  creator_of="calc")

        if only_computer_user_pairs:
            qb.add_projection("computer", "*")
            qb.add_projection("user", "*")
            returnresult = qb.distinct().all()
        else:
            qb.add_projection("calc", "*")
            if limit is not None:
                qb.limit(limit)
            returnresult = qb.all()
            returnresult = zip(*returnresult)[0]
        return returnresult
Ejemplo n.º 28
0
    def calculation_cleanworkdir(self, *args):
        """
        Clean the working directory of calculations by removing all the content of the
        associated RemoteFolder node. Calculations can be identified by pk with the -k flag
        or by specifying limits on the modification times with -p/-o flags
        """
        import argparse

        parser = argparse.ArgumentParser(
            prog=self.get_full_command_name(),
            description="""
                Clean all content of all output remote folders of calculations,
                passed as a list of pks, or identified by modification time.

                If a list of calculation PKs is not passed with the -k option, one or both
                of the -p and -o options has to be specified. If both are specified, a logical
                AND is done between the two, i.e. the calculations that will be cleaned have been
                modified AFTER [-p option] days from now but BEFORE [-o option] days from now.
                Passing the -f option will prevent the confirmation dialog from being prompted.
                """
        )
        parser.add_argument(
            '-k', '--pk', metavar='PK', type=int, nargs='+', dest='pk',
            help='The principal key (PK) of the calculations of which to clean the work directory'
        )
        parser.add_argument(
            '-f', '--force', action='store_true',
            help='Force the cleaning (no prompt)'
        )
        parser.add_argument(
            '-p', '--past-days', metavar='N', type=int, action='store', dest='past_days',
            help='Include calculations that have been modified within the last N days', 
        )
        parser.add_argument(
            '-o', '--older-than', metavar='N', type=int, action='store', dest='older_than',
            help='Include calculations that have been modified more than N days ago',
        )
        parser.add_argument(
            '-c', '--computers', metavar='label', nargs='+', type=str, action='store', dest='computer',
            help='Include only calculations that were ran on these computers'
        )

        if not is_dbenv_loaded():
            load_dbenv()

        from aiida.backends.utils import get_automatic_user
        from aiida.backends.utils import get_authinfo
        from aiida.common.utils import query_yes_no
        from aiida.orm.computer import Computer as OrmComputer
        from aiida.orm.user import User as OrmUser
        from aiida.orm.calculation import Calculation as OrmCalculation
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.utils import timezone
        import datetime

        parsed_args = parser.parse_args(args)

        # If a pk is given then the -o & -p options should not be specified
        if parsed_args.pk is not None:
            if (parsed_args.past_days is not None or parsed_args.older_than is not None):
                print("You cannot specify both a list of calculation pks and the -p or -o options")
                return

        # If no pk is given then at least one of the -o & -p options should be specified
        else:
            if (parsed_args.past_days is None and parsed_args.older_than is None):
                print("You should specify at least a list of calculations or the -p, -o options")
                return

        qb_user_filters = dict()
        user = OrmUser(dbuser=get_automatic_user())
        qb_user_filters["email"] = user.email

        qb_computer_filters = dict()
        if parsed_args.computer is not None:
            qb_computer_filters["name"] = {"in": parsed_args.computer}

        qb_calc_filters = dict()
        if parsed_args.past_days is not None:
            pd_ts = timezone.now() - datetime.timedelta(days=parsed_args.past_days)
            qb_calc_filters["mtime"] = {">": pd_ts}
        if parsed_args.older_than is not None:
            ot_ts = timezone.now() - datetime.timedelta(days=parsed_args.older_than)
            qb_calc_filters["mtime"] = {"<": ot_ts}
        if parsed_args.pk is not None:
            print("parsed_args.pk: ", parsed_args.pk)
            qb_calc_filters["id"] = {"in": parsed_args.pk}

        qb = QueryBuilder()
        qb.append(OrmCalculation, tag="calc",
                  filters=qb_calc_filters,
                  project=["id", "uuid", "attributes.remote_workdir"])
        qb.append(OrmComputer, computer_of="calc", tag="computer",
                  project=["*"],
                  filters=qb_computer_filters)
        qb.append(OrmUser, creator_of="calc", tag="user",
                  project=["*"],
                  filters=qb_user_filters)

        no_of_calcs = qb.count()
        if no_of_calcs == 0:
            print("No calculations found with the given criteria.")
            return

        print("Found {} calculations with the given criteria.".format(
            no_of_calcs))

        if not parsed_args.force:
            if not query_yes_no("Are you sure you want to clean the work "
                                "directory?", "no"):
                return

        # get the uuids of all calculations matching the filters
        calc_list_data = qb.dict()

        # get all computers associated to the calc uuids above, and load them
        # we group them by uuid to avoid computer duplicates
        comp_uuid_to_computers = {_["computer"]["*"].uuid: _["computer"]["*"] for _ in calc_list_data}

        # now build a dictionary with the info of folders to delete
        remotes = {}
        for computer in comp_uuid_to_computers.values():
            # initialize a key of info for a given computer
            remotes[computer.name] = {'transport': get_authinfo(
                computer=computer, aiidauser=user._dbuser).get_transport(),
                                      'computer': computer,
            }

            # select the calc pks done on this computer
            this_calc_pks = [_["calc"]["id"] for _ in calc_list_data
                             if _["computer"]["*"].id == computer.id]

            this_calc_uuids = [unicode(_["calc"]["uuid"])
                               for _ in calc_list_data
                               if _["computer"]["*"].id == computer.id]

            remote_workdirs = [_["calc"]["attributes.remote_workdir"]
                               for _ in calc_list_data
                               if _["calc"]["id"] in this_calc_pks
                               if _["calc"]["attributes.remote_workdir"]
                               is not None]

            remotes[computer.name]['remotes'] = remote_workdirs
            remotes[computer.name]['uuids'] = this_calc_uuids

        # now proceed to cleaning
        for computer, dic in remotes.iteritems():
            print("Cleaning the work directory on computer {}.".format(computer))
            counter = 0
            t = dic['transport']
            with t:
                remote_user = remote_user = t.whoami()
                aiida_workdir = dic['computer'].get_workdir().format(
                    username=remote_user)

                t.chdir(aiida_workdir)
                # Hardcoding the sharding equal to 3 parts!
                existing_folders = t.glob('*/*/*')

                folders_to_delete = [i for i in existing_folders if
                                     i.replace("/", "") in dic['uuids']]

                for folder in folders_to_delete:
                    t.rmtree(folder)
                    counter += 1
                    if counter % 20 == 0 and counter > 0:
                        print("Deleted work directories: {}".format(counter))

            print("{} remote folder(s) cleaned.".format(counter))
Ejemplo n.º 29
0
def create(outfile, computers, groups, nodes, group_names, no_parents,
           no_calc_outputs, overwrite, archive_format):
    """
    Export nodes and groups of nodes to an archive file for backup or sharing purposes
    """
    import sys
    from aiida.backends.utils import load_dbenv
    load_dbenv()
    from aiida.orm import Group, Node, Computer
    from aiida.orm.querybuilder import QueryBuilder
    from aiida.orm.importexport import export, export_zip

    node_id_set = set(nodes)
    group_dict = dict()

    if group_names:
        qb = QueryBuilder()
        qb.append(Group,
                  tag='group',
                  project=['*'],
                  filters={'name': {
                      'in': group_names
                  }})
        qb.append(Node, tag='node', member_of='group', project=['id'])
        res = qb.dict()

        group_dict.update({
            group['group']['*'].name: group['group']['*'].dbgroup
            for group in res
        })
        node_id_set.update([node['node']['id'] for node in res])

    if groups:
        qb = QueryBuilder()
        qb.append(Group,
                  tag='group',
                  project=['*'],
                  filters={'id': {
                      'in': groups
                  }})
        qb.append(Node, tag='node', member_of='group', project=['id'])
        res = qb.dict()

        group_dict.update({
            group['group']['*'].name: group['group']['*'].dbgroup
            for group in res
        })
        node_id_set.update([node['node']['id'] for node in res])

    # The db_groups that correspond to what was searched above
    dbgroups_list = group_dict.values()

    # Getting the nodes that correspond to the ids that were found above
    if len(node_id_set) > 0:
        qb = QueryBuilder()
        qb.append(Node,
                  tag='node',
                  project=['*'],
                  filters={'id': {
                      'in': node_id_set
                  }})
        node_list = [node[0] for node in qb.all()]
    else:
        node_list = list()

    # Check if any of the nodes wasn't found in the database.
    missing_nodes = node_id_set.difference(node.id for node in node_list)
    for node_id in missing_nodes:
        print >> sys.stderr, (
            'WARNING! Node with pk={} not found, skipping'.format(node_id))

    # The dbnodes of the above node list
    dbnode_list = [node.dbnode for node in node_list]

    if computers:
        qb = QueryBuilder()
        qb.append(Computer,
                  tag='comp',
                  project=['*'],
                  filters={'id': {
                      'in': set(computers)
                  }})
        computer_list = [computer[0] for computer in qb.all()]
        missing_computers = set(computers).difference(
            computer.id for computer in computer_list)

        for computer_id in missing_computers:
            print >> sys.stderr, (
                'WARNING! Computer with pk={} not found, skipping'.format(
                    computer_id))
    else:
        computer_list = []

    # The dbcomputers of the above computer list
    dbcomputer_list = [computer.dbcomputer for computer in computer_list]

    what_list = dbnode_list + dbcomputer_list + dbgroups_list
    additional_kwargs = dict()

    if archive_format == 'zip':
        export_function = export_zip
        additional_kwargs.update({'use_compression': True})
    elif archive_format == 'zip-uncompressed':
        export_function = export_zip
        additional_kwargs.update({'use_compression': False})
    elif archive_format == 'tar.gz':
        export_function = export
    else:
        print >> sys.stderr, 'invalid --archive-format value {}'.format(
            archive_format)
        sys.exit(1)

    try:
        export_function(what=what_list,
                        also_parents=not no_parents,
                        also_calc_outputs=not no_calc_outputs,
                        outfile=outfile,
                        overwrite=overwrite,
                        **additional_kwargs)
    except IOError as e:
        print >> sys.stderr, 'IOError: {}'.format(e.message)
        sys.exit(1)
Ejemplo n.º 30
0
    def test_simple_query_django_1(self):
        """
        Testing a simple query
        """
        from aiida.orm.querybuilder import QueryBuilder
        from aiida.orm.calculation.job import JobCalculation
        from aiida.orm import Node, Data, Calculation
        from datetime import datetime
        from aiida.common.links import LinkType

        n1 = Data()
        n1.label = 'node1'
        n1._set_attr('foo', ['hello', 'goodbye'])
        n1.store()

        n2 = Calculation()
        n2.label = 'node2'
        n2._set_attr('foo', 1)
        n2.store()

        n3 = Data()
        n3.label = 'node3'
        n3._set_attr('foo', 1.0000)  # Stored as fval
        n3.store()

        n4 = Calculation()
        n4.label = 'node4'
        n4._set_attr('foo', 'bar')
        n4.store()

        n5 = Data()
        n5.label = 'node5'
        n5._set_attr('foo', None)
        n5.store()

        n2.add_link_from(n1, link_type=LinkType.INPUT)
        n3.add_link_from(n2, link_type=LinkType.CREATE)

        n4.add_link_from(n3, link_type=LinkType.INPUT)
        n5.add_link_from(n4, link_type=LinkType.CREATE)

        qb1 = QueryBuilder()
        qb1.append(Node, filters={'attributes.foo': 1.000})

        self.assertEqual(len(qb1.all()), 2)

        qb2 = QueryBuilder()
        qb2.append(Data)
        self.assertEqual(qb2.count(), 3)

        qb2 = QueryBuilder()
        qb2.append(type='data.Data.')
        self.assertEqual(qb2.count(), 3)

        qb3 = QueryBuilder()
        qb3.append(Node, project='label', tag='node1')
        qb3.append(Node, project='label', tag='node2')
        self.assertEqual(qb3.count(), 4)

        qb4 = QueryBuilder()
        qb4.append(Calculation, tag='node1')
        qb4.append(Data, tag='node2')
        self.assertEqual(qb4.count(), 2)

        qb5 = QueryBuilder()
        qb5.append(Data, tag='node1')
        qb5.append(Calculation, tag='node2')
        self.assertEqual(qb5.count(), 2)

        qb6 = QueryBuilder()
        qb6.append(Data, tag='node1')
        qb6.append(Data, tag='node2')
        self.assertEqual(qb6.count(), 0)