def test_composite_and_missing_in_db(self): """ Pass through branches of a CompositeAndFilter not indexed by the data source. """ self.maxDiff = 10000 result = adorn_filter( None, self.ds, ContactsFilter( locations=CompositeAndFilter({ 'city': MatchFilter('indy'), 'state': MatchFilter('IN'), 'county': MatchFilter('marion'), }))) self.assertEqual([{ 'locations': { 'city': ['indy'], 'state': ['IN'], 'county': ['marion'], }, }], self.ds.adorn_calls) self.assertEqual( ContactsFilter(locations=CompositeAndFilter( { 'city': MatchFilter({ 'value': 'indy', 'display': 'Indy' }), 'state': MatchFilter({ 'value': 'IN', 'display': 'Indiana' }), 'county': MatchFilter('marion'), })), result)
def test_composite_and(self): """Test adorning a CompositeAndFilter.""" self.maxDiff = 10000 result = adorn_filter( None, self.ds, ContactsFilter( locations=CompositeAndFilter({ 'city': MatchFilter('indy'), 'state': MatchFilter('IN'), }))) self.assertEqual([{ 'locations': { 'city': ['indy'], 'state': ['IN'], }, }], self.ds.adorn_calls) self.assertEqual( ContactsFilter(locations=CompositeAndFilter( { 'city': MatchFilter({ 'value': 'indy', 'display': 'Indy' }), 'state': MatchFilter({ 'value': 'IN', 'display': 'Indiana' }), })), result)
def test_city_filter(self): """Test that city filter is built properly.""" result = self.driver.build_filter('{"locations": {"city": "Indy"}}') self.assertEquals( ContactsFilter( locations=CompositeAndFilter({'city': MatchFilter('Indy')})), result)
def test_clone_without_full(self): """Cloning should remove certain fields.""" filter = ContactsFilter(tags=['C'], locations=CompositeAndFilter({ 'city': MatchFilter('A'), 'state': MatchFilter('B') })) expected_with_city = ContactsFilter(tags=['C'], locations=CompositeAndFilter( {'city': MatchFilter('A')})) expected_with_state = ContactsFilter(tags=['C'], locations=CompositeAndFilter( {'state': MatchFilter('B')})) self.assertEqual(expected_with_state, filter.clone_without_city()) self.assertEqual(expected_with_city, filter.clone_without_state())
def test_clone_without_full(self): """Cloning should remove certain fields.""" filter = PartnersFilter(tags=AndGroupFilter( [OrGroupFilter([MatchFilter('C')])]), locations=CompositeAndFilter({ 'city': MatchFilter('A'), 'state': MatchFilter('B') })) expected_with_city = PartnersFilter( tags=AndGroupFilter([OrGroupFilter([MatchFilter('C')])]), locations=CompositeAndFilter({'city': MatchFilter('A')})) expected_with_state = PartnersFilter( tags=AndGroupFilter([OrGroupFilter([MatchFilter('C')])]), locations=CompositeAndFilter({'state': MatchFilter('B')})) self.assertEqual(expected_with_state, filter.clone_without_city()) self.assertEqual(expected_with_city, filter.clone_without_state())
def test_clone_without_partners(self): locations_filter = CompositeAndFilter({ 'city': MatchFilter('A'), 'state': MatchFilter('B') }) filter = ContactsFilter(partner=[1, 2, 3], locations=locations_filter) expected_without_partners = ContactsFilter(locations=locations_filter) self.assertEqual(expected_without_partners, filter.clone_without_partner())
def test_help_city(self): """Check city help works and ignores current city filter.""" ds = ContactsDataSource() recs = ds.help_city( self.company, ContactsFilter(locations=CompositeAndFilter( {'city': MatchFilter('Los Angeles')})), "angel") actual = {r['value'] for r in recs} self.assertEqual({'Los Angeles'}, actual)
def test_help_state(self): """Check state help works and ignores current state filter.""" ds = ContactsDataSource() recs = ds.help_state( self.company, ContactsFilter( locations=CompositeAndFilter({'state': MatchFilter('zz')})), "i") actual = {r['value'] for r in recs} self.assertEqual({'IL', 'IN'}, actual)
def test_filter_by_city(self): """Should show only commrecs with correct city.""" ds = CommRecordsDataSource() recs = ds.run_unaggregated( self.company, CommRecordsFilter(locations=CompositeAndFilter( {'city': MatchFilter('Los Angeles')})), []) subjects = {r['subject'] for r in recs} expected = {self.record_3.subject} self.assertEqual(expected, subjects)
def test_filter_by_city(self): """Should show only contacts with correct city.""" ds = ContactsDataSource() recs = ds.run_unaggregated( self.company, ContactsFilter(locations=CompositeAndFilter( {'city': MatchFilter('Los Angeles')})), []) names = [r['name'] for r in recs] expected = [self.sue.name] self.assertEqual(expected, names)
def test_composite_and_group_missing_db_field(self): """ Failing to pass a db field to a compsite and field should result in no comparison for that field. """ qs = MockQuerySet() filt = CompositeAndFilter({'a': MatchFilter('b'), 'c': NoFilter()}) result = apply_filter_to_queryset(qs, filt, {'a': 'aaa', 'b': 'bbb'}) self.assert_filters_equals([Q(aaa='b')], result.filters) result = apply_filter_to_queryset(qs, filt, {'b', 'bbb'}) self.assert_filters_equals([], result.filters)
def test_filter_by_state(self): """Should show only partners with correct state.""" ds = PartnersDataSource() recs = ds.run_unaggregated( self.company, PartnersFilter( locations=CompositeAndFilter({ 'state': MatchFilter('CA'), })), []) names = [r['name'] for r in recs] expected = [self.partner_b.name] self.assertEqual(expected, names)
def clone_without_city(self): """City help needs it's filter cleared before searching for help. Return a new CommRecordsFilter without the current city filter applied. """ new_root = dict(self.__dict__) locations = new_root.get('locations', NoFilter()) if locations: new_locations = dict(locations.field_map) if 'city' in locations.field_map: del new_locations['city'] new_root['locations'] = CompositeAndFilter(new_locations) return CommRecordsFilter(**new_root)
def clone_without_state(self): """State help needs it's filter cleared before searching for help. Return a new PartnersFilter without the current state filter applied. """ new_root = dict(self.__dict__) locations = new_root.get('locations', NoFilter()) if locations: new_locations = dict(locations.field_map) if 'state' in locations.field_map: del new_locations['state'] new_root['locations'] = CompositeAndFilter(new_locations) return PartnersFilter(**new_root)
def test_passthrough_missing_composite_and(self): """ Pass through when all branches of a CompositeAndFilter are not indexed by the data source. """ result = adorn_filter( None, self.ds, ContactsFilter(partner=CompositeAndFilter({ 'a': MatchFilter(1), 'b': MatchFilter(2) }))) self.assertEqual([{ 'partner': { 'a': [1], 'b': [2] }, }], self.ds.adorn_calls) self.assertEqual( ContactsFilter(partner=CompositeAndFilter({ 'a': MatchFilter(1), 'b': MatchFilter(2) })), result)
def test_plain_filter_composite_and(self): """Convert CompositeAndFilter to plain object.""" filt = ContactsFilter( locations=CompositeAndFilter({ 'city': MatchFilter('indy'), 'state': MatchFilter('IN'), })) self.assertEqual({ 'locations': { 'city': 'indy', 'state': 'IN', }, }, plain_filter(filt))
def test_composite_and_group(self): """CompositeAndFilter should result in and'ed Q.""" qs = MockQuerySet() filt = CompositeAndFilter({ 'a': MatchFilter('b'), 'c': OrGroupFilter([MatchFilter('d'), MatchFilter('e')]) }) result = apply_filter_to_queryset(qs, filt, {'a': 'aaa', 'c': 'ccc'}) self.assert_filters_equals([ Q(aaa='b') & (Q(ccc='d') | Q(ccc='e')), ], result.filters)
def test_composite_and_partial_filter(self): """ Only one branch of a CompositeAndFilter is specified. """ self.maxDiff = 10000 result = adorn_filter( None, self.ds, ContactsFilter( locations=CompositeAndFilter({ 'city': MatchFilter('indy'), }))) self.assertEqual([{ 'locations': { 'city': ['indy'], }, }], self.ds.adorn_calls) self.assertEqual( ContactsFilter(locations=CompositeAndFilter({ 'city': MatchFilter({ 'value': 'indy', 'display': 'Indy' }), })), result)
def build_filter(self, filter_json): """ Build a filter for the given filter_json filters look like: { filter_column1: some filter_spec..., filter_column2: another filter_spec..., } filter_specs can be any of: missing/null/[]: Empty lists, nulls, or missing keys are all interpreted as do not filter on this column. [date, date]: A list of two dates is date range. The filter class must have a key type of 'date'. 1/"a": A single choice. [1, 2, 3]: A list of choices is an "or group". Match any of the choices. [[1, 2, 3], [4, 5, 6]]: A list of lists of choices is an "and group". Must match any of the first AND any of each of the subsequent groups. {'field1': some filter}: Match each field with the filter given: A CompositeAndFilter. The filter class must have a key type of 'composite'. {'nolink': True}: A special object which means to find objects not linked to the objects in this column. i.e. If this is a tags column, find untagged objects. """ kwargs = {} filter_spec = json.loads(filter_json) filter_type = self.ds.filter_type() types = filter_type.filter_key_types() for key, data in filter_spec.iteritems(): key_type = types.get(key, None) if key_type is None: # empty lists are the same as no filter if data == []: continue elif (isinstance(data, list) and len(data) > 0 and isinstance(data[0], list)): kwargs[key] = AndGroupFilter([ OrGroupFilter([ MatchFilter(v) for v in or_f ]) for or_f in data]) elif isinstance(data, list) and len(data) > 0: kwargs[key] = OrGroupFilter([ MatchFilter(v) for v in data ]) elif isinstance(data, dict) and 'nolink' in data: kwargs[key] = UnlinkedFilter() else: kwargs[key] = MatchFilter(data) elif key_type == 'date_range': date_strings = filter_spec.get(key, None) dates = [ (datetime.strptime(d, "%m/%d/%Y") if d else None) for d in date_strings] kwargs[key] = DateRangeFilter(dates) elif key_type == 'composite': value_map = { col: MatchFilter(value) for col, value in filter_spec.get(key, {}).iteritems() } kwargs[key] = CompositeAndFilter(value_map) else: message = 'DataSource %s has unknown filter key type %s' % ( self.ds.__class__.__name__, repr(key_type)) raise KeyError(message) return filter_type(**kwargs)
def test_composite_and_group_none(self): """Falsey branches of CompositeAndFilter should not compare.""" qs = MockQuerySet() filt = CompositeAndFilter({'a': MatchFilter('b'), 'c': NoFilter()}) result = apply_filter_to_queryset(qs, filt, {'a': 'aaa', 'c': 'ccc'}) self.assert_filters_equals([Q(aaa='b')], result.filters)
def test_composite_and_group_empty(self): """Empty CompositeAndFilter should result in no comparisons.""" qs = MockQuerySet() filt = CompositeAndFilter({}) result = apply_filter_to_queryset(qs, filt, {}) self.assert_filters_equals([], result.filters)