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
def get_bands_and_parents_structure(self, args): """ Returns bands and closest parent structure """ from collections import defaultdict from django.db.models import Q from aiida.common.utils import grouper from aiida.backends.djsite.db import models from aiida.orm.backend import construct_backend from aiida.orm.data.structure import (get_formula, get_symbols_string) from aiida.orm.data.array.bands import BandsData backend = construct_backend() query_group_size = 100 q_object = None if args.all_users is False: q_object = Q(user__id=backend.users.get_automatic_user().id) else: q_object = Q() self.query_past_days(q_object, args) self.query_group(q_object, args) bands_list = BandsData.query(q_object).distinct().order_by('ctime') bands_list_data = bands_list.values_list('pk', 'label', 'ctime') # split data in chunks grouped_bands_list_data = grouper(query_group_size, [(_[0], _[1], _[2]) for _ in bands_list_data]) entry_list = [] for this_chunk in grouped_bands_list_data: # gather all banddata pks pks = [_[0] for _ in this_chunk] # get the closest structures (WITHOUT DbPath) q_object = Q(type='data.structure.StructureData.') structure_dict = get_closest_parents(pks, q_object, chunk_size=1) struc_pks = [structure_dict[pk] for pk in pks] # query for the attributes needed for the structure formula attr_query = Q(key__startswith='kinds') | Q( key__startswith='sites') attrs = models.DbAttribute.objects.filter( attr_query, dbnode__in=struc_pks).values_list('dbnode__pk', 'key', 'datatype', 'tval', 'fval', 'ival', 'bval', 'dval') results = defaultdict(dict) for attr in attrs: results[attr[0]][attr[1]] = { "datatype": attr[2], "tval": attr[3], "fval": attr[4], "ival": attr[5], "bval": attr[6], "dval": attr[7] } # organize all of it in a dictionary deser_data = {} for k in results: deser_data[k] = models.deserialize_attributes( results[k], sep=models.DbAttribute._sep) # prepare the printout for ((bid, blabel, bdate), struc_pk) in zip(this_chunk, struc_pks): if struc_pk is not None: # Exclude structures by the elements if args.element is not None: all_kinds = [ k['symbols'] for k in deser_data[struc_pk]['kinds'] ] all_symbols = [ item for sublist in all_kinds for item in sublist ] if not any([s in args.element for s in all_symbols]): continue if args.element_only is not None: all_kinds = [ k['symbols'] for k in deser_data[struc_pk]['kinds'] ] all_symbols = [ item for sublist in all_kinds for item in sublist ] if not all( [s in all_symbols for s in args.element_only]): continue # build the formula symbol_dict = { k['name']: get_symbols_string(k['symbols'], k['weights']) for k in deser_data[struc_pk]['kinds'] } try: symbol_list = [ symbol_dict[s['kind_name']] for s in deser_data[struc_pk]['sites'] ] 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>>" # cycle if we imposed the filter on elements if args.element is not None or args.element_only is not None: continue else: formula = "<<UNKNOWN>>" entry_list.append([ str(bid), str(formula), bdate.strftime('%d %b %Y'), blabel ]) return entry_list
def list_structures(elements, raw, formulamode, past_days, groups, all_users): """ List stored StructureData objects """ from aiida.orm.data.structure import StructureData from aiida.orm.data.structure import (get_formula, get_symbols_string) from tabulate import tabulate elements_only = False lst = _list(StructureData, PROJECT_HEADERS, elements, elements_only, formulamode, past_days, groups, all_users) entry_list = [] for [pid, label, akinds, asites] in lst: # If symbols are defined there is a filtering of the structures # based on the element # When QueryBuilder will support this (attribute)s filtering, # it will be pushed in the query. if elements is not None: all_symbols = [_["symbols"][0] for _ in akinds] if not any([s in elements for s in all_symbols]): continue if elements_only: echo.echo_critical("Not implemented elements-only search") # 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 site in asites: symbol_list.append(symbol_dict[site['kind_name']]) formula = get_formula(symbol_list, mode=formulamode) # If for some reason there is no kind with the name # referenced by the site except KeyError: formula = "<<UNKNOWN>>" entry_list.append([str(pid), str(formula), label]) counter = 0 struct_list_data = list() if not raw: struct_list_data.append(PROJECT_HEADERS) for entry in entry_list: for i, value in enumerate(entry): if isinstance(value, list): entry[i] = ",".join(value) for i in range(len(entry), len(PROJECT_HEADERS)): entry.append(None) counter += 1 struct_list_data.extend(entry_list) if raw: echo.echo(tabulate(struct_list_data, tablefmt='plain')) else: echo.echo(tabulate(struct_list_data, headers="firstrow")) echo.echo("\nTotal results: {}\n".format(counter))