# -*- coding: utf-8 -*-
"""A class providing a convenient interface for subsets
"""
from itertools import zip_longest
import logging
from .subset import _Subset
from .table import _Table
logger = logging.getLogger(__name__)
[docs]
def grouped(iterable, n):
"s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,...s3n-1), ..."
return zip_longest(*[iter(iterable)] * n)
class _Subsets(_Table):
"""The Subsets class handles the subsets for a single configuration.
Parameters
----------
configuration : _Configuration
The configuration to work with.
logger : Logger
The logger to use, defaults to the one for this module.
"""
def __init__(self, configuration, logger=logger):
self._configuration = configuration
self.logger = logger
self._cid = configuration.id
self._system_db = configuration.system_db
super().__init__(self._system_db, "subset")
self._sa_table = self._system_db["subset_atom"]
def __eq__(self, other):
"""Return a boolean if this object is equal to another"""
raise NotImplementedError()
@property
def n_subsets(self):
"""The number of subsets for a configuration.
Returns
-------
int
The number of subsets in the configuration.
"""
sql = """\
SELECT COUNT(*)
FROM subset
WHERE configuration = ?
"""
self.cursor.execute(sql, (self._cid,))
result = self.cursor.fetchone()[0]
return result
def append(self, n=None, **kwargs):
"""Append one or more rows
The keywords are the names of attributes and the value to use.
The default value for any attributes not given is used unless it is
'None' in which case an error is thrown. It is an error if there is not
an exisiting attribute corresponding to any given as arguments.
Parameters
----------
n : int = None
The number of rows to create, defaults to the number of items
in the longest attribute given.
kwargs :
any number <attribute name> = <value> keyword arguments giving
existing attributes and values.
Returns
-------
[int]
The ids of the created rows.
"""
kwargs["configuration"] = self._cid
ids = super().append(n, **kwargs)
return ids
def create(self, template, atoms=None, templateatoms=None):
"""Create a subset given a template and optionally atoms.
Parameters
----------
template : int or _Template
The template for the subset
atoms : [int] = None
Optional list of atom ids to connect to the subset.
templateatoms : [int] = None
Optional list of template atoms to connect to the atoms
in the subset.
Returns
-------
_Subset
The subset.
"""
if isinstance(template, int):
tid = template
else:
tid = template.id
sid = self.append(template=tid)[0]
if atoms is not None:
if templateatoms is not None:
self._sa_table.append(
subset=sid, atom=atoms, templateatom=templateatoms
)
else:
self._sa_table.append(subset=sid, atom=atoms)
return _Subset(self.system_db, sid)
def delete(self, ids):
"""Remove one or more subsets
Parameters
----------
ids : [int] of 'all'
The subsets to delete.
Returns
-------
None
"""
if ids == "all":
sql = """\
DELETE FROM subset
WHERE configuration = ?
"""
self.db.execute(sql, (self._cid,))
else:
if isinstance(ids, int):
self.db.execute("DELETE FROM subset WHERE id = ?", (ids,))
else:
self.db.executemany("DELETE FROM subset WHERE id = ?", ids)
def generate(self, template):
"""Generate the subsets matching a full template.
Parameters
----------
template : int or _Template
The template.
Returns
-------
[_Subset]
The subsets.
"""
pass
def get(self, template):
"""Get the subsets given a template.
Parameters
----------
template : int or _Template
The template.
Returns
-------
[_Subset]
The subsets.
"""
if isinstance(template, int):
tid = template
else:
tid = template.id
return [_Subset(self.system_db, x) for x in self.get_ids(tid)]
def get_ids(self, template):
"""Find ids of subsets given a template.
Parameters
----------
template : int or _Template
The template for the subset
Returns
-------
[int]
The ids of the subsets, and empty list if there are none.
"""
if isinstance(template, int):
tid = template
else:
tid = template.id
sql = """
SELECT id FROM subset
WHERE template = ?
AND configuration = ?
"""
return [x[0] for x in self.db.execute(sql, (tid, self._cid))]
def get_counts(self, configuration="all"):
"""Get the counts of subsets per template for configurations.
Parameters
----------
configuration : int or MolSystem._Configuration or "all"
The configuration, as an object or its id, or "all" for all
configurations. Default is "all".
Returns
-------
[[configuration_id, name, count]]
"""
if configuration == "all":
sql = (
"SELECT subset.configuration, name, count(1) FROM subset, template "
" WHERE template = template.id "
" GROUP BY subset.configuration, name "
" ORDER BY subset.configuration"
)
return [[*x] for x in self.db.execute(sql)]
else:
raise NotImplementedError("get_counts only take 'all' configurations")