Skip to content

Commit

Permalink
Remove explicit definition of type_string in Group construction
Browse files Browse the repository at this point in the history
Since the group type strings are now based on the entry point names, the
existing group type strings in the database have to be migrated:

 * `user` -> `core.group`
 * `data.upf.family` -> `core.upf`
 * `auto.import` -> `core.import`
 * `auto.run` -> `core.run`
  • Loading branch information
sphuber committed Mar 31, 2020
1 parent d7e3f2c commit 8d2bc61
Show file tree
Hide file tree
Showing 22 changed files with 285 additions and 90 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ python .ci/test_plugin_testcase.py # uses custom unittest test runner
AIIDA_TEST_PROFILE=test_$AIIDA_TEST_BACKEND pytest .ci/pytest

# main aiida-core tests
AIIDA_TEST_PROFILE=test_$AIIDA_TEST_BACKEND pytest tests
AIIDA_TEST_PROFILE=test_$AIIDA_TEST_BACKEND pytest tests -sv
44 changes: 44 additions & 0 deletions aiida/backends/djsite/db/migrations/0044_dbgroup_type_string.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved. #
# This file is part of the AiiDA code. #
# #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida-core #
# For further information on the license, see the LICENSE.txt file #
# For further information please visit http://www.aiida.net #
###########################################################################
# pylint: disable=invalid-name,too-few-public-methods
"""Migration after the `Group` class became pluginnable and so the group `type_string` changed."""

# pylint: disable=no-name-in-module,import-error
from django.db import migrations
from aiida.backends.djsite.db.migrations import upgrade_schema_version

REVISION = '1.0.44'
DOWN_REVISION = '1.0.43'

forward_sql = [
"""UPDATE db_dbgroup SET type_string = 'core.group' WHERE type_string = 'user';""",
"""UPDATE db_dbgroup SET type_string = 'core.upf' WHERE type_string = 'data.upf';""",
"""UPDATE db_dbgroup SET type_string = 'core.import' WHERE type_string = 'auto.import';""",
"""UPDATE db_dbgroup SET type_string = 'core.run' WHERE type_string = 'auto.run';""",
]

reverse_sql = [
"""UPDATE db_dbgroup SET type_string = 'user' WHERE type_string = 'core.group';""",
"""UPDATE db_dbgroup SET type_string = 'data.upf' WHERE type_string = 'core.upf';""",
"""UPDATE db_dbgroup SET type_string = 'auto.import' WHERE type_string = 'core.import';""",
"""UPDATE db_dbgroup SET type_string = 'auto.run' WHERE type_string = 'core.run';""",
]


class Migration(migrations.Migration):
"""Migration after the update of group `type_string`"""
dependencies = [
('db', '0043_default_link_label'),
]

operations = [
migrations.RunSQL(sql='\n'.join(forward_sql), reverse_sql='\n'.join(reverse_sql)),
upgrade_schema_version(REVISION, DOWN_REVISION),
]
2 changes: 1 addition & 1 deletion aiida/backends/djsite/db/migrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class DeserializationException(AiidaException):
pass


LATEST_MIGRATION = '0043_default_link_label'
LATEST_MIGRATION = '0044_dbgroup_type_string'


def _update_schema_version(version, apps, _):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
"""Migration after the `Group` class became pluginnable and so the group `type_string` changed.
Revision ID: bf591f31dd12
Revises: 118349c10896
Create Date: 2020-03-31 10:00:52.609146
"""
# pylint: disable=no-name-in-module,import-error,invalid-name,no-member
from alembic import op
from sqlalchemy.sql import text

forward_sql = [
"""UPDATE db_dbgroup SET type_string = 'core.group' WHERE type_string = 'user';""",
"""UPDATE db_dbgroup SET type_string = 'core.upf' WHERE type_string = 'data.upf';""",
"""UPDATE db_dbgroup SET type_string = 'core.import' WHERE type_string = 'auto.import';""",
"""UPDATE db_dbgroup SET type_string = 'core.run' WHERE type_string = 'auto.run';""",
]

reverse_sql = [
"""UPDATE db_dbgroup SET type_string = 'user' WHERE type_string = 'core.group';""",
"""UPDATE db_dbgroup SET type_string = 'data.upf' WHERE type_string = 'core.upf'';""",
"""UPDATE db_dbgroup SET type_string = 'auto.import' WHERE type_string = 'core.import';""",
"""UPDATE db_dbgroup SET type_string = 'auto.run' WHERE type_string = 'core.run';""",
]

# revision identifiers, used by Alembic.
revision = 'bf591f31dd12'
down_revision = '118349c10896'
branch_labels = None
depends_on = None


def upgrade():
"""Migrations for the upgrade."""
conn = op.get_bind()
statement = text('\n'.join(forward_sql))
conn.execute(statement)


def downgrade():
"""Migrations for the downgrade."""
conn = op.get_bind()
statement = text('\n'.join(reverse_sql))
conn.execute(statement)
11 changes: 1 addition & 10 deletions aiida/cmdline/commands/cmd_data/cmd_upf.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,13 @@ def upf_listfamilies(elements, with_description):
"""
from aiida import orm
from aiida.plugins import DataFactory
from aiida.orm.nodes.data.upf import UPFGROUP_TYPE

UpfData = DataFactory('upf') # pylint: disable=invalid-name
query = orm.QueryBuilder()
query.append(UpfData, tag='upfdata')
if elements is not None:
query.add_filter(UpfData, {'attributes.element': {'in': elements}})
query.append(
orm.Group,
with_node='upfdata',
tag='group',
project=['label', 'description'],
filters={'type_string': {
'==': UPFGROUP_TYPE
}}
)
query.append(orm.UpfFamily, with_node='upfdata', tag='group', project=['label', 'description'])

query.distinct()
if query.count() > 0:
Expand Down
22 changes: 4 additions & 18 deletions aiida/cmdline/commands/cmd_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from aiida.common.exceptions import UniquenessError
from aiida.cmdline.commands.cmd_verdi import verdi
from aiida.cmdline.params import options, arguments, types
from aiida.cmdline.params import options, arguments
from aiida.cmdline.utils import echo
from aiida.cmdline.utils.decorators import with_dbenv

Expand Down Expand Up @@ -178,18 +178,6 @@ def group_show(group, raw, limit, uuid):
echo.echo(tabulate(table, headers=header))


@with_dbenv()
def valid_group_type_strings():
from aiida.orm import GroupTypeString
return tuple(i.value for i in GroupTypeString)


@with_dbenv()
def user_defined_group():
from aiida.orm import GroupTypeString
return GroupTypeString.USER.value


@verdi_group.command('list')
@options.ALL_USERS(help='Show groups for all users, rather than only for the current user')
@click.option(
Expand All @@ -204,8 +192,7 @@ def user_defined_group():
'-t',
'--type',
'group_type',
type=types.LazyChoice(valid_group_type_strings),
default=user_defined_group,
default='core.group',
help='Show groups of a specific type, instead of user-defined groups. Start with semicolumn if you want to '
'specify aiida-internal type'
)
Expand Down Expand Up @@ -330,9 +317,8 @@ def group_list(
def group_create(group_label):
"""Create an empty group with a given name."""
from aiida import orm
from aiida.orm import GroupTypeString

group, created = orm.Group.objects.get_or_create(label=group_label, type_string=GroupTypeString.USER.value)
group, created = orm.Group.objects.get_or_create(label=group_label)

if created:
echo.echo_success("Group created with PK = {} and name '{}'".format(group.id, group.label))
Expand All @@ -351,7 +337,7 @@ def group_copy(source_group, destination_group):
Note that the destination group may not exist."""
from aiida import orm

dest_group, created = orm.Group.objects.get_or_create(label=destination_group, type_string=source_group.type_string)
dest_group, created = orm.Group.objects.get_or_create(label=destination_group)

# Issue warning if destination group is not empty and get user confirmation to continue
if not created and not dest_group.is_empty:
Expand Down
4 changes: 2 additions & 2 deletions aiida/cmdline/params/types/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ def orm_class_loader(self):

@with_dbenv()
def convert(self, value, param, ctx):
from aiida.orm import Group, GroupTypeString
from aiida.orm import Group
try:
group = super().convert(value, param, ctx)
except click.BadParameter:
if self._create_if_not_exist:
group = Group(label=value, type_string=GroupTypeString.USER.value)
group = Group(label=value)
else:
raise

Expand Down
9 changes: 2 additions & 7 deletions aiida/orm/autogroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,20 @@
# For further information on the license, see the LICENSE.txt file #
# For further information please visit http://www.aiida.net #
###########################################################################


from aiida.common import exceptions, timezone
from aiida.orm import GroupTypeString


current_autogroup = None

VERDIAUTOGROUP_TYPE = GroupTypeString.VERDIAUTOGROUP_TYPE.value

# TODO: make the Autogroup usable to the user, and not only to the verdi run


class Autogroup:
"""
An object used for the autogrouping of objects.
The autogrouping is checked by the Node.store() method.
In the store(), the Node will check if current_autogroup is != None.
If so, it will call Autogroup.is_to_be_grouped, and decide whether to put it in a group.
Such autogroups are going to be of the VERDIAUTOGROUP_TYPE.
Such autogroups are going to be of the `AutoGroup` class.
The exclude/include lists, can have values 'all' if you want to include/exclude all classes.
Otherwise, they are lists of strings like: calculation.quantumespresso.pw, data.array.kpoints, ...
Expand Down
21 changes: 18 additions & 3 deletions aiida/orm/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# For further information please visit http://www.aiida.net #
###########################################################################
""" AiiDA Group entites"""

from abc import ABCMeta
from enum import Enum
import warnings
Expand All @@ -22,7 +21,7 @@
from . import entities
from . import users

__all__ = ('Group', 'GroupTypeString')
__all__ = ('Group', 'GroupTypeString', 'AutoGroup', 'ImportGroup', 'UpfFamily')


def load_group_class(type_string):
Expand Down Expand Up @@ -62,7 +61,11 @@ def __new__(mcs, name, bases, namespace, **kwargs):


class GroupTypeString(Enum):
"""A simple enum of allowed group type strings."""
"""A simple enum of allowed group type strings.
TODO deprecated
"""

UPFGROUP_TYPE = 'data.upf'
IMPORTGROUP_TYPE = 'auto.import'
Expand Down Expand Up @@ -409,3 +412,15 @@ def get_schema():
'type': 'unicode'
}
}


class AutoGroup(Group):
"""Group to be used to contain all nodes generated during the execution of a script using `verdi run`."""


class ImportGroup(Group):
"""Group to be used to contain all nodes from an export archive that has been imported."""


class UpfFamily(Group):
"""Group that represents a pseudo potential family containing `UpfData` nodes."""
2 changes: 1 addition & 1 deletion aiida/orm/implementation/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def get_or_create(cls, *args, **kwargs):
:return: (group, created) where group is the group (new or existing,
in any case already stored) and created is a boolean saying
"""
res = cls.query(name=kwargs.get('name'), type_string=kwargs.get('type_string'))
res = cls.query(name=kwargs.get('name'))

if not res:
return cls.create(*args, **kwargs), True
Expand Down
30 changes: 8 additions & 22 deletions aiida/orm/nodes/data/upf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,14 @@
# For further information please visit http://www.aiida.net #
###########################################################################
"""Module of `Data` sub class to represent a pseudopotential single file in UPF format and related utilities."""

import json
import re

from upf_to_json import upf_to_json

from aiida.common.lang import classproperty
from aiida.orm import GroupTypeString
from .singlefile import SinglefileData

__all__ = ('UpfData',)

UPFGROUP_TYPE = GroupTypeString.UPFGROUP_TYPE.value

REGEX_UPF_VERSION = re.compile(r"""
\s*<UPF\s+version\s*="
(?P<version>.*)">
Expand Down Expand Up @@ -107,9 +101,7 @@ def upload_upf_family(folder, group_label, group_description, stop_if_existing=T
nfiles = len(filenames)

automatic_user = orm.User.objects.get_default()
group, group_created = orm.Group.objects.get_or_create(
label=group_label, type_string=UPFGROUP_TYPE, user=automatic_user
)
group, group_created = orm.UpfFamily.objects.get_or_create(label=group_label, user=automatic_user)

if group.user.email != automatic_user.email:
raise UniquenessError(
Expand Down Expand Up @@ -312,12 +304,6 @@ def get_or_create(cls, filepath, use_first=False, store_upf=True):

return (pseudos[0], False)

@classproperty
def upffamily_type_string(cls):
"""Return the type string used for UPF family groups."""
# pylint: disable=no-self-argument,no-self-use
return UPFGROUP_TYPE

def store(self, *args, **kwargs):
"""Store the node, reparsing the file so that the md5 and the element are correctly reset."""
# pylint: disable=arguments-differ
Expand Down Expand Up @@ -388,11 +374,11 @@ def set_file(self, file, filename=None):

def get_upf_family_names(self):
"""Get the list of all upf family names to which the pseudo belongs."""
from aiida.orm import Group
from aiida.orm import UpfFamily
from aiida.orm import QueryBuilder

query = QueryBuilder()
query.append(Group, filters={'type_string': {'==': self.upffamily_type_string}}, tag='group', project='label')
query.append(UpfFamily, tag='group', project='label')
query.append(UpfData, filters={'id': {'==': self.id}}, with_group='group')
return [label for label, in query.all()]

Expand Down Expand Up @@ -465,9 +451,9 @@ def get_upf_group(cls, group_label):
:param group_label: the family group label
:return: the `Group` with the given label, if it exists
"""
from aiida.orm import Group
from aiida.orm import UpfFamily

return Group.get(label=group_label, type_string=cls.upffamily_type_string)
return UpfFamily.get(label=group_label)

@classmethod
def get_upf_groups(cls, filter_elements=None, user=None):
Expand All @@ -480,12 +466,12 @@ def get_upf_groups(cls, filter_elements=None, user=None):
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 Group
from aiida.orm import UpfFamily
from aiida.orm import QueryBuilder
from aiida.orm import User

builder = QueryBuilder()
builder.append(Group, filters={'type_string': {'==': cls.upffamily_type_string}}, tag='group', project='*')
builder.append(UpfFamily, tag='group', project='*')

if user:
builder.append(User, filters={'email': {'==': user}}, with_group='group')
Expand All @@ -496,7 +482,7 @@ def get_upf_groups(cls, filter_elements=None, user=None):
if filter_elements is not None:
builder.append(UpfData, filters={'attributes.element': {'in': filter_elements}}, with_group='group')

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

return [group for group, in builder.all()]

Expand Down
4 changes: 2 additions & 2 deletions aiida/orm/nodes/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ def store(self, with_transaction=True, use_cache=None): # pylint: disable=argum
self._store(with_transaction=with_transaction, clean=True)

# Set up autogrouping used by verdi run
from aiida.orm.autogroup import current_autogroup, Autogroup, VERDIAUTOGROUP_TYPE
from aiida.orm.autogroup import current_autogroup, Autogroup
from aiida.orm import Group

if current_autogroup is not None:
Expand All @@ -1034,7 +1034,7 @@ def store(self, with_transaction=True, use_cache=None): # pylint: disable=argum
if current_autogroup.is_to_be_grouped(self):
group_label = current_autogroup.get_group_name()
if group_label is not None:
group = Group.objects.get_or_create(label=group_label, type_string=VERDIAUTOGROUP_TYPE)[0]
group = Group.objects.get_or_create(label=group_label)[0]
group.add_nodes(self)

return self
Expand Down
Loading

0 comments on commit 8d2bc61

Please sign in to comment.