Source code for schrodinger.ui.qt.filter_dialog_dir.filter_core
"""
A simple, JSON-serializable property-based filter used by the filter_dialog
module and compatible backends.
"""
import json
[docs]class Criterion(object):
"""
Object representing a single criteria of a filter. It consists of a property
name and a "limiter", which is a filter string for str types, a range
of values for int/real types and True or False value for boolean types.
"""
[docs] def __init__(self, prop, limiter, checked=True):
"""
Initialize criterion.
:param prop: name of property used in this criterion
:type prop: str
:param limiter: criterion limiting values: True or False if property is
boolen, str if property is string and tuple that contains min and
max values if property is numerical.
:type limiter: str or bool or Tuple
:param checked: indicates whether this criterion is 'checked'. True
by default
:type checked: bool
"""
self.prop = prop
self.limiter = limiter
self.checked = checked
[docs] def valueMatches(self, value):
"""
Return True if the given value matches the criteria; False otherwise.
:param value: Value to test
:type value: str or None
"""
if value is None:
# Filter out ligands that don't have this property
return False
type_char = self.prop[0]
if type_char == 's':
if self.limiter == '':
# When no matching string was specified always return True
return True
else:
return self.limiter in value
elif type_char in ('i', 'r'):
min_v, max_v = self.limiter
if min_v is None:
return value < max_v
elif max_v is None:
return value > min_v
else:
return value >= min_v and value <= max_v
elif type_char == 'b':
return value == self.limiter
[docs]class Filter(object):
"""
A filter objects is made up of a name and a list of criteria.
"""
[docs] def __init__(self, name, criteria=None):
"""
:param name: Filter name
:type name: str
:param criteria: List of criteria to filter by
:type criteria: list of `Criterion`
"""
self.name = name
if criteria:
self.criteria = criteria
else:
self.criteria = []
[docs] def doPropsMatch(self, lig_props):
"""
Returns True if the given dict of property names/values matches all
criteria; returns False if at least one criteria doesn't match.
"""
for criterion in self.criteria:
if criterion.checked:
value = lig_props.get(criterion.prop)
# NOTE: value will be None if the ligand is missing the property,
# which will cause a no-match.
if not criterion.valueMatches(value):
return False
return True
[docs] def serialize(self):
"""
Encode the criteria from this filter into a JSON string.
"""
return json.dumps(self._getFilterDict())
[docs] @classmethod
def fromJSON(cls, json_str):
"""
Create a Filter instance from the given JSON string.
:param json_str: JSON string specifying the filtering criteria.
If None an empty filter is returned.
:type json_str: str or None
"""
if json_str is None:
return cls('empty_filter')
loaded_dict = json.loads(json_str)
name = loaded_dict['name']
criteria = [
Criterion(c['property'], c['limiter'], c.get('checked', True))
for c in loaded_dict['criteria']
]
return cls(name, criteria)
[docs] @classmethod
def fromFile(cls, filename):
"""
Create a Filter instance from a file (may be None).
:param filename: name of JSON file specifying the filtering criteria.
If None, an empty filter is returned.
:type filename: str or NoneType
:rtype: `Filter`
"""
if filename:
with open(filename) as fh:
return cls.fromJSON(fh.read())
else:
return cls('empty filter')
[docs] def toFile(self, filename):
"""
Write filter object to JSON file.
:param filename: name of JSON file, where filter object will be stored.
:type filename: str
"""
filter_dict = self._getFilterDict()
with open(filename, 'w') as outfile:
json.dump(filter_dict, outfile)
def _getFilterDict(self):
"""
Returns dictionary that contains filter name and criteria.
:return: filter object as dictionary
:rtype: dict
"""
criteria = [{
'property': c.prop,
'limiter': c.limiter,
'checked': c.checked
} for c in self.criteria]
return {'name': self.name, 'criteria': criteria}
[docs] def isChecked(self):
"""
Checks whether any criterion is toggled on.
:return: True if any criterion is toggled 'on' and False otherwise.
:rtype: bool
"""
return any(c.checked for c in self.criteria)