Skip to content

Commit b14243e

Browse files
authored
GroupPath: a utility to work with virtual Group hierarchies (#3613)
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 b14243e

File tree

9 files changed

+732
-1
lines changed

9 files changed

+732
-1
lines changed

.ci/workchains.py

+1
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

+1-1
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

+69
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

+11
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)