Skip to content

Commit ec6d7ea

Browse files
chrisjsewellsphuber
authored andcommitted
GroupPath: a utility to work with virtual Group hierarchies
Groups can be used to store nodes in AiiDA, but do not have any builtin hierarchy themselves. However, often it may be useful to think of groups as folders on a filesystem and the nodes within them as the files. Building this functionality directly on the database would require significant changes, but a virtual hierarchy based on the group labels can be readily provided. This is what the new utility class `GroupPath` facilitates. It allows group labels to be interpreted as the hierarchy of groups. Example: consider one has groups with the following labels group/sub/a group/sub/b group/other/c One could see this as the group `group` containing the sub groups `sub` and `other`, with `sub` containing `a` and `b` itself. The `GroupPath` class allows one to exploit this hierarchical naming:: path = GroupPath('group') path.sub.a.get_group() # will return group with label `group/sub/a` It can also be used to create groups that do not yet exist: path = GroupPath() path.some.group.get_or_create_group() This will create a `Group` with the label `some/group`. The `GroupPath` class implements many other useful methods to make the traversing and manipulating of groups a lot easier.
1 parent 38c4684 commit ec6d7ea

File tree

9 files changed

+732
-1
lines changed

9 files changed

+732
-1
lines changed

.ci/workchains.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# For further information on the license, see the LICENSE.txt file #
88
# For further information please visit http://www.aiida.net #
99
###########################################################################
10+
# pylint: disable=invalid-name
1011
from aiida.common import AttributeDict
1112
from aiida.engine import calcfunction, workfunction, WorkChain, ToContext, append_, while_, ExitCode
1213
from aiida.engine import BaseRestartWorkChain, process_handler, ProcessHandlerReport

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ confidence=
5050
# --enable=similarities". If you want to run only the classes checker, but have
5151
# no Warning level messages displayed, use"--disable=all --enable=classes
5252
# --disable=W"
53-
disable=bad-continuation,locally-disabled,useless-suppression,django-not-available,bad-option-value,logging-format-interpolation,no-else-raise,import-outside-toplevel
53+
disable=bad-continuation,locally-disabled,useless-suppression,django-not-available,bad-option-value,logging-format-interpolation,no-else-raise,import-outside-toplevel,cyclic-import
5454

5555
# Enable the message, report, category or checker with the given id(s). You can
5656
# either give multiple identifier separated by comma (,) or put this option

aiida/cmdline/commands/cmd_group.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,72 @@ def group_copy(source_group, destination_group):
361361
# Copy nodes
362362
dest_group.add_nodes(list(source_group.nodes))
363363
echo.echo_success('Nodes copied from group<{}> to group<{}>'.format(source_group.label, dest_group.label))
364+
365+
366+
@verdi_group.group('path')
367+
def verdi_group_path():
368+
"""Inspect groups of nodes, with delimited label paths."""
369+
370+
371+
@verdi_group_path.command('ls')
372+
@click.argument('path', type=click.STRING, required=False)
373+
@click.option('-R', '--recursive', is_flag=True, default=False, help='Recursively list sub-paths encountered')
374+
@click.option('-l', '--long', 'as_table', is_flag=True, default=False, help='List as a table, with sub-group count')
375+
@click.option(
376+
'-d', '--with-description', 'with_description', is_flag=True, default=False, help='Show also the group description'
377+
)
378+
@click.option(
379+
'--no-virtual',
380+
'no_virtual',
381+
is_flag=True,
382+
default=False,
383+
help='Only show paths that fully correspond to an existing group'
384+
)
385+
@click.option(
386+
'-t',
387+
'--type',
388+
'group_type',
389+
type=types.LazyChoice(valid_group_type_strings),
390+
default=user_defined_group,
391+
help='Show groups of a specific type, instead of user-defined groups. Start with semicolumn if you want to '
392+
'specify aiida-internal type'
393+
)
394+
@click.option('--no-warn', is_flag=True, default=False, help='Do not issue a warning if any paths are invalid.')
395+
@with_dbenv()
396+
def group_path_ls(path, recursive, as_table, no_virtual, group_type, with_description, no_warn):
397+
# pylint: disable=too-many-arguments
398+
"""Show a list of existing group paths."""
399+
from aiida.tools.groups.paths import GroupPath, InvalidPath
400+
401+
try:
402+
path = GroupPath(path or '', type_string=group_type, warn_invalid_child=not no_warn)
403+
except InvalidPath as err:
404+
echo.echo_critical(str(err))
405+
406+
if recursive:
407+
children = path.walk()
408+
else:
409+
children = path.children
410+
411+
if as_table or with_description:
412+
from tabulate import tabulate
413+
headers = ['Path', 'Sub-Groups']
414+
if with_description:
415+
headers.append('Description')
416+
rows = []
417+
for child in sorted(children):
418+
if no_virtual and child.is_virtual:
419+
continue
420+
row = [
421+
child.path if child.is_virtual else click.style(child.path, bold=True),
422+
len([c for c in child.walk() if not c.is_virtual])
423+
]
424+
if with_description:
425+
row.append('-' if child.is_virtual else child.get_group().description)
426+
rows.append(row)
427+
echo.echo(tabulate(rows, headers=headers))
428+
else:
429+
for child in sorted(children):
430+
if no_virtual and child.is_virtual:
431+
continue
432+
echo.echo(child.path, bold=not child.is_virtual)

aiida/tools/groups/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# This file is part of the AiiDA code. #
2+
# #
3+
# The code is hosted on GitHub at https://github.com/aiidateam/aiida-core #
4+
# For further information on the license, see the LICENSE.txt file #
5+
# For further information please visit http://www.aiida.net #
6+
###########################################################################
7+
# pylint: disable=wildcard-import,undefined-variable
8+
"""Provides tools for interacting with AiiDA Groups."""
9+
from .paths import *
10+
11+
__all__ = paths.__all__

0 commit comments

Comments
 (0)