示例#1
0
 def test_copy_and_mutate(self):
     schema = mock.Mock()
     chain = FilterChain(schema=schema)
     chain.lookup_descriptions.append(1)
     chain.base_url = 'http://xyz'
     chain['foo'] = 'bar'
     chain['qux'] = 'whee'
     clone = chain.copy()
     # Attributes are copied...
     self.assertEqual(clone.lookup_descriptions, [1])
     self.assertEqual(clone.base_url, chain.base_url)
     self.assertEqual(clone.schema, chain.schema, schema)
     # ... and mutating them doesn't affect the original.
     clone.lookup_descriptions.pop()
     self.assertEqual(chain.lookup_descriptions, [1])
     # Likewise, items are copied, and mutating doesn't affect the copy.
     self.assertEqual(clone['foo'], 'bar')
     del chain['foo']
     self.assertEqual(clone['foo'], 'bar')
     del clone['qux']
     self.assertEqual(chain['qux'], 'whee')
     # Likewise, clearing.
     clone.clear()
     self.assertEqual(clone.items(), [])
     self.assertEqual(chain['qux'], 'whee')
示例#2
0
 def test_add_by_place_id__bad(self):
     chain = FilterChain()
     from django.http import Http404
     self.assertRaises(Http404, chain.add_by_place_id, '')
     self.assertRaises(Http404, chain.add_by_place_id, 'blah')
     self.assertRaises(Http404, chain.add_by_place_id, 'b:123.1')
     self.assertRaises(Http404, chain.add_by_place_id, 'l:9999')
示例#3
0
def get_featured_lookups_by_schema(context):
    """
    Get all :ref:`featured_lookups` names and URLs for them; 
    puts in the context as
    'featured_lookups', a mapping grouped by schema.

    Example:

    .. code-block:: html+django

        {% get_featured_lookups_by_schema %}
        {% for schema, lookups in featured_lookups.items %}
           <ul>{{ schema }}
            {% for info in lookups %}
              <a href="{{ info.url }}">{{ info.lookup }}</a>
               ...
            {% endfor %}
        {% endfor %}
    """
    lookups = {}
    for lookup in Lookup.objects.filter(featured=True).select_related():
        sf = lookup.schema_field
        schema = sf.schema
        filters = FilterChain(schema=schema)
        filters.add(sf, lookup)
        info = {'lookup': lookup.name, 'url': filters.make_url()}
        lookups.setdefault(schema.slug, []).append(info)
    context['featured_lookups'] = lookups
    return u''
示例#4
0
 def _make_chain(self, url):
     request = RequestFactory().get(url)
     crime = models.Schema.objects.get(slug='crime')
     context = {'schema': crime}
     chain = FilterChain(request=request, context=context, schema=crime)
     chain.update_from_request(filter_sf_dict={})
     return chain
示例#5
0
 def render(self, context):
     filterchain = self.filterchain_var.resolve(context)
     if isinstance(filterchain, FilterChain):
         schema = filterchain.schema
     elif isinstance(filterchain, Schema):
         schema = filterchain
         # Note, context['request'] only works if
         # django.core.context_processors.request is enabled in
         # TEMPLATE_CONTEXT_PROCESSORS.
         filterchain = FilterChain(context=context,
                                   request=context['request'],
                                   schema=schema)
     else:
         raise template.TemplateSyntaxError(
             "%r is neither a FilterChain nor a Schema" % filterchain)
     removals = [r.resolve(context) for r in self.removals]
     if self.clear:
         filterchain = filterchain.copy()
         filterchain.clear()
     additions = []
     for key, values in self.additions:
         key = key.resolve(context)
         additions.append((key, [v.resolve(context) for v in values]))
     schema = filterchain.schema
     return filterchain.make_url(additions=additions, removals=removals)
 def _make_chain(self, url):
     request = RequestFactory().get(url)
     argstring = request.path.split('filter/', 1)[-1]
     crime = models.Schema.objects.get(slug='crime')
     context = {'schema': crime}
     chain = FilterChain(request=request, context=context, schema=crime)
     chain.update_from_request(argstring=argstring, filter_sf_dict={})
     return chain
示例#7
0
 def test_no_duplicates(self):
     from ebpub.db.schemafilters import DuplicateFilterError
     self.assertRaises(DuplicateFilterError, FilterChain,
                       (('foo', 'bar'), ('foo', 'bar2')))
     chain = FilterChain()
     chain['foo'] = 'bar'
     self.assertRaises(DuplicateFilterError, chain.__setitem__, 'foo',
                       'bar')
示例#8
0
 def test_add_date__whole_month(self):
     # Special syntax for adding a whole month, convenient for templates
     # where you don't want to have to calculate the end date.
     chain = FilterChain()
     import datetime
     chain.add('date', datetime.date(2011, 8, 13), 'month')
     self.assertEqual(chain['date'].start_date, datetime.date(2011, 8, 1))
     self.assertEqual(chain['date'].end_date, datetime.date(2011, 8, 31))
示例#9
0
 def test_ordering(self):
     chain = FilterChain()
     args = range(10)
     random.shuffle(args)
     for i in args:
         chain[i] = i
     self.assertEqual(chain.items(), [(i, i) for i in args])
     self.assertEqual(chain.keys(), args)
示例#10
0
 def test_add__pubdate(self):
     # Key gets overridden.
     chain = FilterChain()
     import datetime
     chain.add('pubdate', datetime.date(2011, 8, 13))
     self.assert_(chain.has_key('date'))
     self.failIf(chain.has_key('pubdate'))
     self.assertEqual(chain['date'].start_date, datetime.date(2011, 8, 13))
示例#11
0
def bigmap_filter(request, slug, args_from_url):

    s = get_object_or_404(get_schema_manager(request),
                          slug=slug,
                          is_special_report=False)
    if not s.allow_charting:
        return HttpResponse(status=404)

    filter_sf_list = list(
        SchemaField.objects.filter(schema__id=s.id,
                                   is_filter=True).order_by('display_order'))
    textsearch_sf_list = list(
        SchemaField.objects.filter(
            schema__id=s.id, is_searchable=True).order_by('display_order'))

    # Use SortedDict to preserve the display_order.
    filter_sf_dict = SortedDict([(sf.name, sf) for sf in filter_sf_list] +
                                [(sf.name, sf) for sf in textsearch_sf_list])

    # Determine what filters to apply, based on path and/or query string.
    filterchain = FilterChain(request=request, schema=s)
    try:
        filterchain.update_from_request(args_from_url, filter_sf_dict)
        filters_need_more = filterchain.validate()
    except:
        return HttpResponse(status=404)

    config = _decode_map_permalink(request,
                                   show_default_layers=False,
                                   filters=filterchain)

    new_url = filterchain.make_url(
        base_url=reverse('bigmap_filter', args=(slug, )))
    if new_url != request.get_full_path():
        return HttpResponseRedirect(new_url)

    # add in the filter layer
    base_url = reverse('ebpub-schema-filter-geojson', args=(slug, ))
    layer_url = filterchain.make_url(base_url=base_url)
    custom_layer = {
        'url': layer_url,
        'params': {},
        'title': "Custom Filter",
        'visible': True
    }
    config['layers'].append(custom_layer)

    if config['is_widget']:
        return eb_render(request, 'richmaps/embed_bigmap.html',
                         {'map_config': simplejson.dumps(config, indent=2)})
    else:
        return eb_render(request, 'richmaps/bigmap.html',
                         {'map_config': simplejson.dumps(config, indent=2)})
示例#12
0
文件: views.py 项目: slinkp/openblock
def bigmap(request):
    '''
    Big map with all Schemas enabled by default.
    '''
    filterchain = FilterChain(request=request)
    config = _decode_map_permalink(request, filters=filterchain)

    if config['is_widget']:
        return eb_render(request, 'richmaps/embed_bigmap.html',
                         {'map_config': simplejson.dumps(config, indent=2)})
    else:
        return eb_render(request, 'richmaps/bigmap.html',
                         {'map_config': simplejson.dumps(config, indent=2)})
示例#13
0
    def test_filters_for_display(self):
        class Dummy(object):
            def __init__(self, label):
                self.label = label

        chain = FilterChain([
            ('foo', Dummy('yes')),
            ('bar', Dummy(None)),
            ('bat', Dummy('yes also')),
            ('baz', Dummy(None)),
        ])
        self.assertEqual(len(chain.values()), 4)
        self.assertEqual(len(chain.filters_for_display()), 2)
        self.assert_(all([f.label for f in chain.filters_for_display()]))
示例#14
0
    def test_update_from_request__empty(self):
        request = mock.Mock()

        class StubQueryDict(dict):
            def getlist(self, key):
                return []

            def copy(self):
                return StubQueryDict(self.items())

        request.GET = StubQueryDict()
        chain = FilterChain(request=request)
        chain.update_from_request({})
        self.assertEqual(len(chain), 0)
示例#15
0
 def test_add_by_place_id(self, mock_get_object_or_404):
     chain = FilterChain()
     from ebpub.streets.models import Block
     from ebpub.db.schemafilters import BlockFilter
     block = Block(
         city='city',
         street_slug='street_slug',
         pretty_name='pretty_name',
         street_pretty_name='street_pretty_name',
         street='street',
         from_num='123',
         to_num='456',
     )
     mock_get_object_or_404.return_value = block
     chain.add_by_place_id('b:123.1')
     self.assert_(isinstance(chain['location'], BlockFilter))
示例#16
0
 def _get_filterchain(self, context):
     filterchain_or_schema = self.filterchain_var.resolve(context)
     if isinstance(filterchain_or_schema, FilterChain):
         filterchain = filterchain_or_schema
     elif isinstance(filterchain_or_schema, Schema):
         # Note, context['request'] only works if
         # django.core.context_processors.request is enabled in
         # TEMPLATE_CONTEXT_PROCESSORS.
         filterchain = FilterChain(context=context, request=context.get('request'),
                                   schema=filterchain_or_schema)
     else:
         raise template.TemplateSyntaxError(
             "%r is neither a FilterChain nor a Schema" % filterchain_or_schema)
     if self.clear:
         filterchain = filterchain.copy()
         filterchain.clear()
     return filterchain
示例#17
0
    def test_sort(self):
        class Dummy(object):
            def __init__(self, sort_value):
                self._sort_value = sort_value

        dummies = [Dummy(i) for i in range(10)]
        random.shuffle(dummies)
        chain = FilterChain()
        for i in range(10):
            chain[i] = dummies[i]

        self.assertNotEqual(range(10), [v._sort_value for v in chain.values()])

        normalized = chain.copy()
        normalized.sort()
        self.assertEqual(range(10),
                         [v._sort_value for v in normalized.values()])
示例#18
0
    def test_sort__real_filters(self):
        req = mock.Mock()
        qs = mock.Mock()
        schema = mock.Mock()
        context = {'newsitem_qs': qs, 'schema': schema}
        from ebpub.db.schemafilters import TextSearchFilter, BoolFilter
        from ebpub.db.schemafilters import LookupFilter, LocationFilter
        from ebpub.db.schemafilters import DateFilter

        def mock_schemafield(name):
            # mock.Mock(name='foo') does something magic, but I just
            # want to set the name attribute.
            sf = mock.Mock()
            sf.name = name
            return sf

        all_filters = [
            TextSearchFilter(
                req,
                context,
                qs,
                'hi',
                schemafield=mock_schemafield(name='mock text sf')),
            BoolFilter(req,
                       context,
                       qs,
                       'yes',
                       schemafield=mock_schemafield(name='mock bool sf')),
            LookupFilter(req,
                         context,
                         qs,
                         schemafield=mock_schemafield(name='mock lookup sf')),
            LocationFilter(req, context, qs, 'neighborhoods'),
            DateFilter(req, context, qs, '2011-04-11', '2011-04-12'),
        ]
        chain = FilterChain([(item.slug, item) for item in all_filters])
        ordered_chain = chain.copy()
        ordered_chain.sort()
        self.assertEqual(ordered_chain.keys(), [
            'date', 'mock bool sf', 'location', 'mock lookup sf',
            'mock text sf'
        ])
示例#19
0
文件: views.py 项目: slinkp/openblock
def bigmap_filter(request, slug):
    """
    Big map with just one Schema (identified by ``slug``) enabled by
    default.
    """
    s = get_object_or_404(get_schema_manager(request),
                          slug=slug,
                          is_special_report=False)
    if not s.allow_charting:
        return HttpResponse(status=404)

    filter_sf_dict = _get_filter_schemafields(s)

    # Determine what filters to apply, based on path and/or query string.
    filterchain = FilterChain(request=request, schema=s)
    try:
        filterchain.update_from_request(filter_sf_dict)
        filters_need_more = filterchain.validate()
    except:
        logger.exception("Unhandled error")
        return HttpResponse(status=404)

    config = _decode_map_permalink(request,
                                   show_default_layers=False,
                                   filters=filterchain)

    # TODO: This can leave in permalink params eg. 'i', even if there
    # is also 'ids', because it doesn't recognize those as being the
    # same.
    new_url = filterchain.make_url(
        base_url=reverse('bigmap_filter', args=(slug, )))
    if new_url != request.get_full_path():
        return HttpResponseRedirect(new_url)

    if config['is_widget']:
        return eb_render(request, 'richmaps/embed_bigmap.html',
                         {'map_config': simplejson.dumps(config, indent=2)})
    else:
        return eb_render(request, 'richmaps/bigmap.html',
                         {'map_config': simplejson.dumps(config, indent=2)})
示例#20
0
文件: views.py 项目: slinkp/openblock
def _decode_map_permalink(request, show_default_layers=True, filters=None):
    """
    Permalinks for the big map, with more compact query parameters.

    Returns a map_config dictionary.

    Accepted parameters:

    c - map center, separated by underscore, eg. c=-92.28283_38.95658

    z - map zoom, eg. z=12

    l - layers to display on load, comma- or dash-separated,
        eg. l=p13,t32,p1 or eg. l=p12345-t7-t9,
        where p => place layer
        and t => schema ("type") layer

    i - items to load specificially by id, comma- or dash-separated,
        eg. i=t1234-t456

    p - popup center, with underscore, eg. p=-92.3438_38.9658
    f - popup feature, eg. f=t1234 or f=p1234
        where p = a place and t = a news item

    start_date - start date (inclusive) %m/%d/%Y
    end_date - end date (inclusive) %m/%d/%Y
    d - duration in days (overridden by end date), eg. d=7

    x - show as 'widget', just the map and nothign around it.
        Takes no value, eg. x
    w - width of map (widget only), in pixels
    h - height of map (widget only), in pixels
    v - limits what map controls are displayed (widget only).
        By default, widget-stype map shows none of these.
        Possible values, joined with no separator:
        l - layer switcher
        h - list of headlines next to map
        p - permalink
        eg. to turn them all on: v=lhp


    """
    params = request.GET
    schemas = set()
    place_types = set()
    lids = params.get("l", None)
    show_custom_layer = False
    if lids is not None:
        no_layers_specified = False
        try:
            pat = re.compile('(\w\d+)')
            for lid in pat.findall(lids):
                layer_type = lid[0]
                layer_id = int(lid[1:])
                if layer_type == 'p':
                    place_types.add(layer_id)
                elif layer_type == 't':
                    schemas.add(layer_id)
                elif layer_type == 'c':
                    show_custom_layer = True
        except:
            pass
    else:
        no_layers_specified = True

    # map center
    center = params.get("c", None)
    if center:
        try:
            center = [float(x) for x in center.split('_')][0:2]
        except:
            pass

    # map zoom level
    zoom = params.get("z", None)
    if zoom:
        try:
            zoom = float(zoom)
        except:
            pass

    # popup
    popup_info = None
    popup_center = params.get("p", None)
    popup_feature = params.get("f", None)
    if popup_center and popup_feature:
        try:
            popup_center = [float(x) for x in popup_center.split('_')][0:2]
            feature_type = popup_feature[0]
            feature_id = int(popup_feature[1:])
            if feature_type == 'p':
                openblock_type = 'place'
            elif feature_type == 't':
                openblock_type = 'newsitem'

            popup_info = {
                'id': feature_id,
                'openblock_type': openblock_type,
                'lonlat': [popup_center[0], popup_center[1]]
            }
        except:
            popup_center = None
            popup_feature = None

    # start and end date range
    default_interval = datetime.timedelta(days=7)
    duration = params.get('d')
    if duration is not None:
        try:
            duration = datetime.timedelta(days=int(duration))
        except (TypeError, ValueError):
            duration = default_interval
    else:
        duration = default_interval
    default_enddate = datetime.date.today()
    default_startdate = default_enddate - duration

    startdate = params.get('start_date')
    if startdate is not None:
        for format in ('%m/%d/%Y', '%Y-%m-%d'):
            try:
                startdate = datetime.datetime.strptime(startdate,
                                                       format).date()
                break
            except ValueError:
                pass
        if isinstance(startdate, basestring):
            startdate = None

    enddate = params.get('end_date')
    if enddate is not None:
        for format in ('%m/%d/%Y', '%Y-%m-%d'):
            try:
                enddate = datetime.datetime.strptime(enddate, format).date()
                break
            except ValueError:
                pass
        if isinstance(enddate, basestring):
            enddate = None

    # The filters argument can override startdate & enddate.
    if startdate is None and enddate is None and filters:
        date_filter = filters.get('date') or filters.get('pubdate')
        if date_filter:
            startdate = date_filter.start_date
            enddate = date_filter.end_date

    if startdate is None and enddate is None:
        enddate = datetime.date.today()
        startdate = enddate - duration
    elif startdate is None:
        startdate = enddate - duration
    elif enddate is None:
        enddate = startdate + duration

    if enddate < startdate:
        enddate = startdate + duration

    # inject date range into filters if none was specified:
    if filters and filters.get('date') is None:
        filters.add('date', startdate, enddate)

    api_startdate = startdate.strftime("%Y-%m-%d")
    api_enddate = (enddate + datetime.timedelta(days=1)).strftime("%Y-%m-%d")

    layers = []

    if (startdate != default_startdate) or (enddate != default_enddate):
        show_custom_layer = True

    # All available place layers.
    for place_type in PlaceType.objects.filter(is_mappable=True).all():
        layers.append({
            'id':
            'p%d' % place_type.id,
            'title':
            place_type.plural_name,
            'url':
            reverse('place_detail_json', args=[place_type.slug]),
            'params': {
                'limit': 1000
            },
            'minZoom':
            15,
            'bbox':
            True,
            'visible':
            place_type.id in place_types  # off by default
        })

    # All available NewsItem layers.
    for schema in get_schema_manager(request).all():
        # if filters and 'schema' in filters and filters['schema'].schema == schema:
        #     visible = True
        if no_layers_specified and show_default_layers and not show_custom_layer:
            # default on if no 't' param given
            visible = True
        elif schemas and schema.id in schemas:
            visible = True
        else:
            visible = False
        layers.append({
            'id': 't%d' % schema.id,
            'title': schema.plural_name,
            'url': reverse('map_items_json'),
            'params': {
                'type': schema.slug,
                'limit': 1000,
                'startdate': api_startdate,
                'enddate': api_enddate
            },
            'bbox': False,
            'visible': visible
        })

    # Explicit filtering by ID.
    ids = params.get('i') or u''
    ids = [i.strip() for i in re.split(r'[^\d]+', ids) if i.strip()]
    if ids:
        show_custom_layer = True
        if filters is None:
            filters = FilterChain(request)
        filters.replace('id', *ids)

    # 'Custom' layer. This is a catch-all for all filtering
    # that isn't just enabling a default layer with the default
    # date range.
    # Not visible unless there is something like that to show.
    if filters and sorted(filters.keys()) not in ([], ['date'], [
            'date', 'schema'
    ], ['schema']):
        show_custom_layer = True

    if filters is not None:
        # Don't inspect filters['schema']; that's already covered by schemas above.
        base_url = reverse('map_items_json')
        layer_url = filters.make_url(base_url=base_url)
        # Quick ugly hacks to make the itemquery api happy.
        # Hooray proliferation of spellings.
        layer_url = layer_url.replace('locations=', 'locationid=')
        layer_url = layer_url.replace('start_date=', 'startdate=')
        layer_url = layer_url.replace('end_date=', 'enddate=')

        if 'schema' in filters:
            # Normally, filters.make_url() captures the schema in the
            # path part of the URL. But map_items_json doesn't,
            # so we add a query parameter.
            params = {'type': [s.slug for s in filters['schema'].schemas]}
        else:
            params = {}
        custom_layer = {
            'url': layer_url,
            'params': params,
            'title': u"Custom Filter",
            'visible': show_custom_layer,
            'id': 'c1',
        }
        layers.append(custom_layer)

    is_widget = params.get('x', None) is not None
    controls = {}
    control_list = params.get("v", None)
    if control_list is not None:
        if 'l' in control_list:
            controls['layers'] = True
        if 'h' in control_list:
            controls['headline_list'] = True
        if 'p' in control_list:
            controls['permalink'] = True

    width = params.get("w", None)
    if width:
        try:
            width = int(width)
        except:
            width = None

    height = params.get("h", None)
    if height:
        try:
            height = int(height)
        except:
            height = None

    config = {
        'center':
        center
        or [settings.DEFAULT_MAP_CENTER_LON, settings.DEFAULT_MAP_CENTER_LAT],
        'zoom':
        zoom or settings.DEFAULT_MAP_ZOOM,
        'layers':
        layers,
        'is_widget':
        is_widget,
        'permalink_params': {
            'start_date': startdate.strftime('%m/%d/%Y'),
            'end_date': enddate.strftime('%m/%d/%Y'),
        },
    }

    if 'id' in filters:
        # Put them in the params so the js code can construct, well,
        # permalinks with these ids, on the client side.
        ids = '-'.join(map(str, filters['id'].ids))
        config['permalink_params']['i'] = ids
    if popup_info:
        config['popup'] = popup_info

    if is_widget:
        config['controls'] = controls
        if width is not None:
            config['width'] = width
        if height is not None:
            config['height'] = height

    return config
示例#21
0
 def test_empty(self):
     chain = FilterChain()
     self.assertEqual(chain.items(), [])
示例#22
0
 def test_add__id(self):
     from ebpub.db.schemafilters import IdFilter
     chain = FilterChain()
     chain.add('id', [1, 2, 3])
     self.assert_(isinstance(chain['id'], IdFilter))
示例#23
0
 def test_add__bogus_keyword_arg(self):
     chain = FilterChain()
     self.assertRaises(TypeError, chain.add, 'date', '2011-01-1', foo='bar')
示例#24
0
 def test_add__no_value(self):
     chain = FilterChain()
     self.assertRaises(FilterError, chain.add, 'date')