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)