def bigmap_filter(request, slug): 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) 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)})
def dashboard(request): """ The user's account settings, saved places, alerts, and other personalized stuff. """ custom_message = request.session.get('login_message') if 'login_message' in request.session: del request.session['login_message'] alert_list = EmailAlert.active_objects.filter(user_id=request.user.id) saved_place_list = SavedPlace.objects.filter(user_id=request.user.id) hidden_schema_ids = HiddenSchema.objects.filter( user_id=request.user.id).values('schema_id') hidden_schema_ids = set([x['schema_id'] for x in hidden_schema_ids]) schema_list = [] manager = get_schema_manager(request) for schema in manager.filter( is_special_report=False).order_by('plural_name'): schema_list.append({ 'schema': schema, 'is_hidden': schema.id in hidden_schema_ids }) from ebpub.neighbornews.utils import is_neighbornews_enabled return eb_render( request, 'accounts/dashboard.html', { 'custom_message': custom_message, 'user': request.user, 'alert_list': alert_list, 'saved_place_list': saved_place_list, 'schema_list': schema_list, 'is_neighbornews_enabled': is_neighbornews_enabled, })
def dashboard(request): """ The user's account settings, saved places, alerts, and other personalized stuff. """ custom_message = request.session.get('login_message') if 'login_message' in request.session: del request.session['login_message'] alert_list = EmailAlert.active_objects.filter(user_id=request.user.id) saved_place_list = SavedPlace.objects.filter(user_id=request.user.id) hidden_schema_ids = HiddenSchema.objects.filter(user_id=request.user.id).values('schema_id') hidden_schema_ids = set([x['schema_id'] for x in hidden_schema_ids]) schema_list = [] manager = get_schema_manager(request) for schema in manager.filter(is_special_report=False).order_by('plural_name'): schema_list.append({'schema': schema, 'is_hidden': schema.id in hidden_schema_ids}) from ebpub.neighbornews.utils import is_neighbornews_enabled return eb_render(request, 'accounts/dashboard.html', { 'custom_message': custom_message, 'user': request.user, 'alert_list': alert_list, 'saved_place_list': saved_place_list, 'schema_list': schema_list, 'is_neighbornews_enabled': is_neighbornews_enabled, })
def schema_filter(request, slug, args_from_url): """ List NewsItems for one schema, filtered by various criteria in the URL (date, location, or values of SchemaFields). """ s = get_object_or_404(get_schema_manager(request), slug=slug, is_special_report=False) if not s.allow_charting: return HttpResponsePermanentRedirect(s.url()) context = { 'bodyclass': 'schema-filter', 'bodyid': s.slug, 'schema': s, } # Breadcrumbs. We can assign this early because it's a generator, # so it'll get the full context no matter what. context['breadcrumbs'] = breadcrumbs.schema_filter(context) 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, context=context, schema=s) context['filters'] = filterchain try: filterchain.update_from_request(args_from_url, filter_sf_dict) filters_need_more = filterchain.validate() except FilterError, e: if getattr(e, 'url', None) is not None: return HttpResponseRedirect(e.url) raise Http404(str(e))
def list_types_json(request): """ List the known NewsItem types (Schemas). """ schemas = {} for schema in get_schema_manager(request).all(): attributes = {} for sf in schema.schemafield_set.all(): fieldtype = get_datatype(sf) attributes[sf.name] = { 'pretty_name': sf.smart_pretty_name(), 'type': fieldtype, # TODO: what else? } # TODO: should we enumerate known values of Lookups? schemas[schema.slug] = { 'indefinite_article': schema.indefinite_article, 'last_updated': schema.last_updated.strftime('%Y-%m-%d'), 'name': schema.name, 'plural_name': schema.plural_name, 'slug': schema.slug, 'attributes': attributes, } return APIGETResponse(request, simplejson.dumps(schemas, indent=1), content_type=JSON_CONTENT_TYPE)
def ajax_save_hidden_schema(request): """ Creates a HiddenSchema for request.POST['schema'] and request.user. """ if request.method != 'POST': raise http.Http404() if 'schema' not in request.POST: raise http.Http404('Missing schema') if request.user.is_anonymous(): raise http.Http404('Not logged in') # Validate that the HiddenSchema hasn't already been created for this user, # to avoid duplicates. try: manager = get_schema_manager(request) schema = manager.get(slug=request.POST['schema']) sp = HiddenSchema.objects.get(user_id=request.user.id, schema=schema) except Schema.DoesNotExist: return http.HttpResponse('0') # Schema doesn't exist. except HiddenSchema.DoesNotExist: pass else: return http.HttpResponse('0') # Already exists. HiddenSchema.objects.create(user_id=request.user.id, schema=schema) return http.HttpResponse('1')
def signup(request, *args, **kwargs): place = url_to_place(*args, **kwargs) manager = get_schema_manager(request) if isinstance(place, Block): FormClass, type_code = BlockAlertForm, 'b' else: FormClass, type_code = LocationAlertForm, 'l' email_required = request.user.is_anonymous() if request.method == 'POST': form = FormClass(request.POST, email_required=email_required, request=request) if form.is_valid(): return finish_signup(request, place, form.cleaned_data) else: schema_list = manager.filter(is_special_report=False).order_by('plural_name') schema_ids = [s.id for s in schema_list] form = FormClass( initial={ 'email': 'Enter your e-mail address', 'radius': block_radius_value(request)[1], 'frequency': '1', 'include_new_schemas': True, 'selected_schemas': schema_ids, 'displayed_schemas': schema_ids, }, email_required=email_required, request=request) context = get_place_info_for_request(request, *args, **kwargs) context['map_configuration'] = _preconfigured_map(context); context['form'] = form #context['schema_list'] = schema_list return eb_render(request, 'alerts/signup_form.html', context)
def apply(self): schema_ids = [s.id for s in self.schemas] if self.request: allowed_schema_ids = get_schema_manager( self.request).allowed_schema_ids() schema_ids = set(schema_ids).intersection(allowed_schema_ids) self.qs = self.qs.filter(schema__id__in=schema_ids)
def apply(self): if isinstance(self.schema, list): schemas = self.schema else: schemas = [self.schema] schema_ids = [s.id for s in schemas] if self.request: allowed_schema_ids = get_schema_manager(self.request).allowed_schema_ids() schema_ids = set(schema_ids).intersection(allowed_schema_ids) self.qs = self.qs.filter(schema__id__in=schema_ids)
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)})
def __init__(self, *args, **kwargs): self.email_required = kwargs.pop('email_required', True) request = kwargs.pop('request') forms.Form.__init__(self, *args, **kwargs) if self.email_required: f = forms.EmailField(widget=forms.TextInput(attrs={'id': 'emailinput', 'class': 'textinput placeholder'})) self.fields['email'] = f qs = get_schema_manager(request).all() self.fields['selected_schemas'] = SchemaMultipleChoiceField( widget=forms.CheckboxSelectMultiple, queryset=qs) self.fields['displayed_schemas'] = SchemaMultipleChoiceField( widget=forms.MultipleHiddenInput, queryset=qs)
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) })
def ajax_place_date_chart(request): """ Returns HTML fragment containing a chart of how many news items were added for each day over a short period (length defined by constants.DAYS_SHORT_AGGREGATE_TIMEDELTA). Expects request.GET['pid'] and request.GET['s'] (a Schema ID). """ manager = get_schema_manager(request) try: schema = manager.get(id=int(request.GET['s'])) except (KeyError, ValueError, Schema.DoesNotExist): raise Http404('Invalid Schema') filters = FilterChain(request=request, schema=schema) filters.add_by_place_id(request.GET.get('pid', '')) qs = filters.apply() # These charts are used on eg. the place overview page; there, # they should be smaller than the ones on the schema_detail view; # we don't have room for a full 30 days. date_span = constants.DAYS_SHORT_AGGREGATE_TIMEDELTA if schema.is_event: # Soonest span that includes some. try: qs = qs.filter(item_date__gte=today()).order_by('item_date', 'id') first_item = qs.values('item_date')[0] start_date = first_item['item_date'] except IndexError: # No matching items. start_date = today() end_date = today() + date_span else: # Most recent span that includes some. try: qs = qs.filter(item_date__lte=today()).order_by('-item_date', '-id') last_item = qs.values('item_date')[0] end_date = last_item['item_date'] except IndexError: # No matching items. end_date = today() start_date = end_date - date_span filters.add('date', start_date, end_date) counts = filters.apply().date_counts() date_chart = get_date_chart([schema], start_date, end_date, {schema.id: counts})[0] return render_to_response('db/snippets/date_chart.html', { 'schema': schema, 'date_chart': date_chart, 'filters': filters, })
def schema_list(request): allowed_schemas = get_schema_manager(request).all() schema_list = allowed_schemas.select_related().filter(is_special_report=False).order_by('plural_name') schemafield_list = list(SchemaField.objects.filter(is_filter=True).order_by('display_order')) browsable_locationtype_list = LocationType.objects.filter(is_significant=True) # Populate s_list, which contains a schema and schemafield list for each schema. s_list = [] for s in schema_list: s_list.append({ 'schema': s, 'schemafield_list': [sf for sf in schemafield_list if sf.schema_id == s.id], }) return eb_render(request, 'db/schema_list.html', { 'schema_list': s_list, 'browsable_locationtype_list': browsable_locationtype_list, 'bodyclass': 'schema-list', })
def homepage(request): """Front page of the default OpenBlock theme. """ end_date = today() start_date = end_date - datetime.timedelta(days=settings.DEFAULT_DAYS) end_date += datetime.timedelta(days=1) manager = get_schema_manager(request) sparkline_schemas = list(manager.filter(allow_charting=True, is_special_report=False)) # Order by slug to ensure case-insensitive ordering. (Kind of hackish.) lt_list = LocationType.objects.filter(is_significant=True).order_by('slug').extra(select={'count': 'select count(*) from db_location where is_public=True and location_type_id=db_locationtype.id'}) street_count = Street.objects.count() more_schemas = manager.filter(allow_charting=False).order_by('name') # Get the public records. date_charts = get_date_chart_agg_model(sparkline_schemas, start_date, end_date, AggregateDay) empty_date_charts, non_empty_date_charts = [], [] for chart in date_charts: if chart['total_count']: non_empty_date_charts.append(chart) else: empty_date_charts.append(chart) non_empty_date_charts.sort(lambda a, b: cmp(b['total_count'], a['total_count'])) empty_date_charts.sort(lambda a, b: cmp(a['schema'].plural_name, b['schema'].plural_name)) return eb_render(request, 'homepage.html', { 'location_type_list': lt_list, 'street_count': street_count, 'more_schemas': more_schemas, 'non_empty_date_charts': non_empty_date_charts, 'empty_date_charts': empty_date_charts, 'num_days': settings.DEFAULT_DAYS, 'default_lon': settings.DEFAULT_MAP_CENTER_LON, 'default_lat': settings.DEFAULT_MAP_CENTER_LAT, 'default_zoom': settings.DEFAULT_MAP_ZOOM, 'bodyclass': 'homepage', 'breadcrumbs': breadcrumbs.home({}), 'map_configuration': _preconfigured_map({}) })
def ajax_place_lookup_chart(request): """ Returns HTML fragment -- expects request.GET['pid'] and request.GET['sf'] (a SchemaField ID). """ allowed_schemas = get_schema_manager(request).allowed_schema_ids() try: sf = SchemaField.objects.select_related().get(id=int(request.GET['sf']), schema__id__in=allowed_schemas) except (KeyError, ValueError, SchemaField.DoesNotExist): raise Http404('Invalid SchemaField') filters = FilterChain(request=request, schema=sf.schema) filters.add_by_place_id(request.GET.get('pid', '')) qs = filters.apply() total_count = qs.count() top_values = qs.top_lookups(sf, 10) return render_to_response('db/snippets/lookup_chart.html', { 'lookup': {'sf': sf, 'top_values': top_values}, 'total_count': total_count, 'schema': sf.schema, 'filters': filters, })
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)})
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) })
def _decode_map_permalink(request, show_default_layers=True, filters=None): """ request parameters: c - map center z - map zoom l - layer info p - popup center f - popup feature start_date - start date (inclusive) %m/%d/%Y end_date - end date (inclusive) %m/%d/%Y d - duration in days (overridden by end date) x - show as 'widget' v- limits what map controls are displayed (widget only) w - width of map (widget only) h - height of map (widget only) """ params = request.GET # # enabled item, place layers "l" parameter # # l=p13,t32,p1 ... # # p => place layer # t => schema layer # remaining portion is dbid of place or schema # schemas = set() place_types = set() lids = params.get("l", None) 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) 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 startdate = params.get('start_date') if startdate is not None: try: startdate = datetime.datetime.strptime(startdate, '%m/%d/%Y').date() except: startdate = None enddate = params.get('end_date') if enddate is not None: try: enddate = datetime.datetime.strptime(enddate, '%m/%d/%Y').date() except: enddate = None 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 default_interval = datetime.timedelta(days=7) duration = params.get('d') if duration is not None: try: duration = datetime.timedelta(days=int(duration)) except: duration = default_interval else: duration = default_interval 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) 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 }) api_startdate = startdate.strftime("%Y-%m-%d") api_enddate = (enddate + datetime.timedelta(days=1)).strftime("%Y-%m-%d") for schema in get_schema_manager(request).all(): 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': (no_layers_specified and show_default_layers) or schema.id in schemas # default on if no 't' param given }) 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 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
def _decode_map_permalink(request, show_default_layers=True, filters=None): """ request parameters: c - map center z - map zoom l - layer info p - popup center f - popup feature start_date - start date (inclusive) %m/%d/%Y end_date - end date (inclusive) %m/%d/%Y d - duration in days (overridden by end date) x - show as 'widget' v- limits what map controls are displayed (widget only) w - width of map (widget only) h - height of map (widget only) """ params = request.GET # # enabled item, place layers "l" parameter # # l=p13,t32,p1 ... # # p => place layer # t => schema layer # remaining portion is dbid of place or schema # schemas = set() place_types = set() lids = params.get("l", None) 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) 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 startdate = params.get('start_date') if startdate is not None: try: startdate = datetime.datetime.strptime(startdate, '%m/%d/%Y').date() except: startdate = None enddate = params.get('end_date') if enddate is not None: try: enddate = datetime.datetime.strptime(enddate, '%m/%d/%Y').date() except: enddate = None 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 default_interval = datetime.timedelta(days=7) duration = params.get('d') if duration is not None: try: duration = datetime.timedelta(days=int(duration)) except: duration = default_interval else: duration = default_interval 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) 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 }) api_startdate = startdate.strftime("%Y-%m-%d") api_enddate = (enddate + datetime.timedelta(days=1)).strftime("%Y-%m-%d") for schema in get_schema_manager(request).all(): 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': (no_layers_specified and show_default_layers) or schema.id in schemas # default on if no 't' param given }) 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 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
def allowed_schemas(request): allowed_schemas = get_schema_manager(request).all() return {'allowed_schemas': allowed_schemas}
def place_detail_timeline(request, *args, **kwargs): """ Recent news OR upcoming events for the given Location or Block. """ context, response = _place_detail_normalize_url(request, *args, **kwargs) if response is not None: return response show_upcoming = kwargs.get('show_upcoming') schema_manager = get_schema_manager(request) if show_upcoming: context['breadcrumbs'] = breadcrumbs.place_detail_upcoming(context) else: context['breadcrumbs'] = breadcrumbs.place_detail_timeline(context) is_latest_page = True # Check the query string for the max date to use. Otherwise, fall # back to today. end_date = today() if 'start' in request.GET: try: end_date = parse_date(request.GET['start'], '%m/%d/%Y') is_latest_page = False except ValueError: raise Http404('Invalid date %s' % request.GET['start']) filterchain = FilterChain(request=request, context=context) filterchain.add('location', context['place']) # As an optimization, limit the NewsItems to those on the # last (or next) few days. # And only fetch for relevant schemas - either event-ish or not. if show_upcoming: s_list = schema_manager.filter(is_event=True) start_date = end_date end_date = start_date + datetime.timedelta(days=settings.DEFAULT_DAYS) order_by = 'item_date_date' else: s_list = schema_manager.filter(is_event=False) start_date = end_date - datetime.timedelta(days=settings.DEFAULT_DAYS) order_by = '-item_date_date' filterchain.add('schema', list(s_list)) filterchain.add('date', start_date, end_date) newsitem_qs = filterchain.apply().select_related() # TODO: can this really only be done via extra()? newsitem_qs = newsitem_qs.extra( select={'item_date_date': 'date(db_newsitem.item_date)'}, order_by=(order_by, '-schema__importance', 'schema') )[:constants.NUM_NEWS_ITEMS_PLACE_DETAIL] # We're done filtering, so go ahead and do the query, to # avoid running it multiple times, # per http://docs.djangoproject.com/en/dev/topics/db/optimization ni_list = list(newsitem_qs) schemas_used = list(set([ni.schema for ni in ni_list])) s_list = s_list.filter(is_special_report=False, allow_charting=True).order_by('plural_name') populate_attributes_if_needed(ni_list, schemas_used) if ni_list: next_day = ni_list[-1].item_date - datetime.timedelta(days=1) else: next_day = None hidden_schema_list = [] if not request.user.is_anonymous(): hidden_schema_list = [o.schema for o in HiddenSchema.objects.filter(user_id=request.user.id)] context.update({ 'newsitem_list': ni_list, 'next_day': next_day, 'is_latest_page': is_latest_page, 'hidden_schema_list': hidden_schema_list, 'bodyclass': 'place-detail-timeline', 'bodyid': context.get('place_type') or '', 'filters': filterchain, 'show_upcoming': show_upcoming, }) context['filtered_schema_list'] = s_list context['map_configuration'] = _preconfigured_map(context); response = eb_render(request, 'db/place_detail.html', context) for k, v in context['cookies_to_set'].items(): response.set_cookie(k, v) return response
def schema_filter_geojson(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 FilterError: return HttpResponse(status=400) except BadAddressException: return HttpResponse(status=400) except BadDateException: return HttpResponse(status=400) if filters_need_more: return HttpResponse(status=400) # If there isn't a date filter, add some dates to the queryset, # but NOT to the filterchain, because need to give the user the # option of choosing dates. qs, start_date, end_date = _default_date_filtering(filterchain) if s.is_event: qs = qs.order_by('item_date', 'id') else: qs = qs.order_by('-item_date', '-id') page = request.GET.get('page', None) if page is not None: try: page = int(page) idx_start = (page - 1) * constants.FILTER_PER_PAGE idx_end = page * constants.FILTER_PER_PAGE # Get one extra, so we can tell whether there's a next page. idx_end += 1 except ValueError: return HttpResponse('Invalid Page', status=400) else: idx_start, idx_end = 0, 1000 qs = qs[idx_start:idx_end] cache_key = 'schema_filter_geojson:' + _make_cache_key_from_queryset(qs) cache_seconds = 60 * 5 output = cache.get(cache_key, None) if output is None: output = api_items_geojson(list(qs)) cache.set(cache_key, output, cache_seconds) response = HttpResponse(output, mimetype="application/javascript") patch_response_headers(response, cache_timeout=60 * 5) return response
def schema_detail(request, slug): s = get_object_or_404(get_schema_manager(request), slug=slug) if s.is_special_report: return schema_detail_special_report(request, s) location_type_list = LocationType.objects.filter(is_significant=True).order_by('slug') if s.allow_charting: # For the date range, the end_date is the last non-future date # with at least one NewsItem. try: end_date = NewsItem.objects.filter(schema__id=s.id, item_date__lte=today()).values_list('item_date', flat=True).order_by('-item_date')[0] except IndexError: latest_dates = () date_chart = {} start_date = end_date = None else: start_date = end_date - constants.DAYS_AGGREGATE_TIMEDELTA date_chart = get_date_chart_agg_model([s], start_date, end_date, AggregateDay)[0] latest_dates = [date['date'] for date in date_chart['dates'] if date['count']] # Populate schemafield_list and lookup_list. schemafield_list = list(s.schemafield_set.filter(is_filter=True).order_by('display_order')) # XXX this duplicates part of schema_filter() LOOKUP_MIN_DISPLAYED = 7 LOOKUP_BUFFER = 4 lookup_list = [] for sf in schemafield_list: if not (sf.is_charted and sf.is_lookup): continue top_values = list(AggregateFieldLookup.objects.filter(schema_field__id=sf.id).select_related('lookup').order_by('-total')[:LOOKUP_MIN_DISPLAYED + LOOKUP_BUFFER]) if len(top_values) == LOOKUP_MIN_DISPLAYED + LOOKUP_BUFFER: top_values = top_values[:LOOKUP_MIN_DISPLAYED] has_more = True else: has_more = False lookup_list.append({'sf': sf, 'top_values': top_values, 'has_more': has_more}) location_chartfield_list = [] # Populate location_chartfield_list. for lt in location_type_list: # Collect the locations in the location_type here so we don't have # to query them again in the select_related() below. locations = dict([(loc.id, loc) for loc in lt.location_set.iterator()]) ni_totals = AggregateLocation.objects.filter( schema__id=s.id, location_type__id=lt.id, location__is_public=True).select_related('location').order_by('-total') if ni_totals: # This runs the query. known_count = reduce(operator.add, (n.total for n in ni_totals)) total_count = date_chart.get('total_count', 0) unknown_count = max(0, total_count - known_count) location_chartfield_list.append({'location_type': lt, 'locations': ni_totals[:9], 'unknown': unknown_count}) ni_list = () else: date_chart = {} latest_dates = schemafield_list = lookup_list = location_chartfield_list = () ni_list = list(NewsItem.objects.filter(schema__id=s.id).order_by('-item_date', '-id')[:30]) populate_schema(ni_list, s) populate_attributes_if_needed(ni_list, [s]) textsearch_sf_list = list(SchemaField.objects.filter(schema__id=s.id, is_searchable=True).order_by('display_order')) boolean_lookup_list = [sf for sf in SchemaField.objects.filter(schema__id=s.id, is_filter=True, is_lookup=False).order_by('display_order') if sf.is_type('bool')] templates_to_try = ('db/schema_detail/%s.html' % s.slug, 'db/schema_detail.html') # The HIDE_SCHEMA_INTRO_COOKIE_NAME cookie is a comma-separated list of # schema IDs for schemas whose intro text should *not* be displayed. hide_intro = str(s.id) in request.COOKIES.get(HIDE_SCHEMA_INTRO_COOKIE_NAME, '').split(',') context = { 'schema': s, 'schemafield_list': schemafield_list, 'location_type_list': location_type_list, 'date_chart': date_chart, 'lookup_list': lookup_list, 'location_chartfield_list': location_chartfield_list, 'boolean_lookup_list': boolean_lookup_list, 'search_list': textsearch_sf_list, 'newsitem_list': ni_list, 'latest_dates': latest_dates[-3:], 'hide_intro': hide_intro, 'hide_intro_cookie_name': HIDE_SCHEMA_INTRO_COOKIE_NAME, 'start_date': s.min_date, 'end_date': today(), 'bodyclass': 'schema-detail', 'bodyid': slug, 'filters': FilterChain(schema=s), } context['breadcrumbs'] = breadcrumbs.schema_detail(context) return eb_render(request, templates_to_try, context)
def search(request, schema_slug=''): "Performs a location search and redirects to the address/xy page." # Check whether a schema was provided. if schema_slug: try: schema = get_schema_manager(request).get(slug=schema_slug) except Schema.DoesNotExist: raise Http404('Schema does not exist') url_prefix = schema.url()[:-1] else: schema = None url_prefix = '' # Get the query. q = request.GET.get('q', '').strip() if not q: return HttpResponseRedirect(url_prefix + '/') # TODO: Do something better than redirecting. # For /search/?type=alert, we redirect results to the alert page, not the # place page. if request.GET.get('type', '') == 'alert': url_method = 'alert_url' else: url_method = 'url' # Try to geocode it using full_geocode(). try: result = full_geocode(q, search_places=False) except: # TODO: Naked except clause. pass else: if result['ambiguous']: if result['type'] == 'block': return eb_render(request, 'db/search_invalid_block.html', { 'query': q, 'choices': result['result'], 'street_name': result['street_name'], 'block_number': result['block_number'] }) else: return eb_render(request, 'db/did_you_mean.html', {'query': q, 'choices': result['result']}) elif result['type'] == 'location': return HttpResponseRedirect(url_prefix + getattr(result['result'], url_method)()) elif result['type'] == 'address': # Block if result['result']['block']: return HttpResponseRedirect(url_prefix + getattr(result['result']['block'], url_method)()) # Intersection try: intersection = Intersection.objects.get(id=result['result']['intersection_id']) except Intersection.DoesNotExist: pass else: return HttpResponseRedirect(url_prefix + getattr(intersection, url_method)()) # Failing the geocoding, look in the special-case table. try: special_case = SearchSpecialCase.objects.get(query=normalize(q)) except SearchSpecialCase.DoesNotExist: pass else: if special_case.redirect_to: return HttpResponseRedirect(special_case.redirect_to) else: return eb_render(request, 'db/search_special_case.html', {'query': q, 'special_case': special_case}) # Failing that, display a list of ZIP codes if this looks like a ZIP. if re.search(r'^\s*\d{5}(?:-\d{4})?\s*$', q): z_list = Location.objects.filter(location_type__slug='zipcodes', is_public=True).select_related().order_by('name') if z_list: return eb_render(request, 'db/search_error_zip_list.html', {'query': q, 'zipcode_list': z_list}) # Failing all of that, display the search error page. lt_list = LocationType.objects.filter(is_significant=True).order_by('name') return eb_render(request, 'db/search_error.html', {'query': q, 'locationtype_list': lt_list})
def feed_signup(request, *args, **kwargs): context = get_place_info_for_request(request, *args, **kwargs) context['schema_list'] = get_schema_manager(request).filter(is_special_report=False).order_by('plural_name') return eb_render(request, 'db/feed_signup.html', context)
def place_detail_overview(request, *args, **kwargs): context, response = _place_detail_normalize_url(request, *args, **kwargs) if response is not None: return response schema_manager = get_schema_manager(request) context['breadcrumbs'] = breadcrumbs.place_detail_overview(context) schema_list = SortedDict([(s.id, s) for s in schema_manager.filter(is_special_report=False).order_by('plural_name')]) # needed = set(schema_list.keys()) # We actually want two lists of schemas, since we care whether # they are news-like or future-event-like. import copy eventish_schema_list = copy.deepcopy(schema_list) newsish_schema_list = copy.deepcopy(schema_list) for s_id, schema in schema_list.items(): if schema.is_event: del(newsish_schema_list[s_id]) else: del(eventish_schema_list[s_id]) filterchain = FilterChain(request=request, context=context) filterchain.add('location', context['place']) # Distinguish between past news and upcoming events. # With some preliminary date limiting too. filterchain_news = filterchain.copy() filterchain_news.add('date', today() - datetime.timedelta(days=90), today()) filterchain_events = filterchain.copy() filterchain_events.add('date', today(), today() + datetime.timedelta(days=60)) # Ordering by ID ensures consistency across page views. newsitem_qs = filterchain_news.apply().order_by('-item_date', '-id') events_qs = filterchain_events.apply().order_by('item_date', 'id') # Mapping of schema id -> [schemafields], for building Lookup charts. sf_dict = {} charted_lookups = SchemaField.objects.filter( is_lookup=True, is_charted=True, schema__is_public=True, schema__is_special_report=False) charted_lookups = charted_lookups.values('id', 'schema_id', 'pretty_name') for sf in charted_lookups.order_by('schema__id', 'display_order'): sf_dict.setdefault(sf['schema_id'], []).append(sf) # Now retrieve newsitems per schema. schema_groups, all_newsitems = [], [] for schema in schema_list.values(): if schema.id in newsish_schema_list: newsitems = newsitem_qs.filter(schema__id=schema.id) elif schema.id in eventish_schema_list: newsitems = events_qs.filter(schema__id=schema.id) else: raise RuntimeError("should never get here") newsitems = list(newsitems[:s.number_in_overview]) populate_schema(newsitems, schema) schema_groups.append({ 'schema': schema, 'latest_newsitems': newsitems, 'has_newsitems': bool(newsitems), 'lookup_charts': sf_dict.get(schema.id), }) all_newsitems.extend(newsitems) schema_list = schema_list.values() populate_attributes_if_needed(all_newsitems, schema_list) schema_list = [s for s in schema_list if s.allow_charting] context['schema_groups'] = schema_groups context['filtered_schema_list'] = schema_list context['bodyclass'] = 'place-detail-overview' if context['is_block']: context['bodyid'] = '%s-%s-%s' % (context['place'].street_slug, context['place'].number(), context['place'].dir_url_bit()) else: context['bodyid'] = context['location'].slug response = eb_render(request, 'db/place_overview.html', context) for k, v in context['cookies_to_set'].items(): response.set_cookie(k, v) return response
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
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
def _decode_map_permalink(request, show_default_layers=True, filters=None): """ request parameters: c - map center z - map zoom l - layer info p - popup center f - popup feature start_date - start date (inclusive) %m/%d/%Y end_date - end date (inclusive) %m/%d/%Y d - duration in days (overridden by end date) x - show as 'widget' v- limits what map controls are displayed (widget only) w - width of map (widget only) h - height of map (widget only) """ params = request.GET # # enabled item, place layers "l" parameter # # l=p13,t32,p1 ... # # p => place layer # t => schema layer # remaining portion is dbid of place or schema # schemas = set() place_types = set() lids = params.get("l", None) 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) 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 startdate = params.get("start_date") if startdate is not None: try: startdate = datetime.datetime.strptime(startdate, "%m/%d/%Y").date() except: startdate = None enddate = params.get("end_date") if enddate is not None: try: enddate = datetime.datetime.strptime(enddate, "%m/%d/%Y").date() except: enddate = None 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 default_interval = datetime.timedelta(days=7) duration = params.get("d") if duration is not None: try: duration = datetime.timedelta(days=int(duration)) except: duration = default_interval else: duration = default_interval 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) 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 } ) api_startdate = startdate.strftime("%Y-%m-%d") api_enddate = (enddate + datetime.timedelta(days=1)).strftime("%Y-%m-%d") for schema in get_schema_manager(request).all(): 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": (no_layers_specified and show_default_layers) or schema.id in schemas, # default on if no 't' param given } ) 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 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