From 8d97d2d908a13f195430e52a7e7edaa77698a1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sun, 26 May 2019 11:03:29 +0200 Subject: [PATCH] compute initial content for dashboard (#32064) --- combo_plugin_gnm/__init__.py | 3 - combo_plugin_gnm/signals.py | 34 ------- combo_plugin_gnm/templatetags/gnm.py | 140 +++++++++++++++++---------- debian/50gnm.py | 10 +- 4 files changed, 98 insertions(+), 89 deletions(-) delete mode 100644 combo_plugin_gnm/signals.py diff --git a/combo_plugin_gnm/__init__.py b/combo_plugin_gnm/__init__.py index bee5ea9..4524f52 100644 --- a/combo_plugin_gnm/__init__.py +++ b/combo_plugin_gnm/__init__.py @@ -27,9 +27,6 @@ class AppConfig(django.apps.AppConfig): name = __name__ verbose_name = _('GNM Extension') - def ready(self): - from . import signals - def get_before_urls(self): from . import urls return urls.urlpatterns diff --git a/combo_plugin_gnm/signals.py b/combo_plugin_gnm/signals.py deleted file mode 100644 index 40e5c02..0000000 --- a/combo_plugin_gnm/signals.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -# combo-plugin-gnm - Combo GNM plugin -# Copyright (C) 2017-2019 Entr'ouvert -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Affero General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from django.db.models.signals import post_save -from django.dispatch import receiver - -from combo.middleware import get_request -from combo.profile.models import Profile - -@receiver(post_save, sender=Profile) -def new_profile(sender, instance=None, created=False, **kwargs): - if not created: - return - request = get_request() - saml_info = request.session.get('mellon_session', {}) - if False: - # if profile has an address push it to the favourite places datastore - saml_info['address'][0] - - # fill dashboard with suggestions diff --git a/combo_plugin_gnm/templatetags/gnm.py b/combo_plugin_gnm/templatetags/gnm.py index 7e363ee..14af88c 100644 --- a/combo_plugin_gnm/templatetags/gnm.py +++ b/combo_plugin_gnm/templatetags/gnm.py @@ -24,6 +24,7 @@ import random import re from dateutil.parser import parse as dateutil_parse +from pyproj import Geod from requests import RequestException from django import template @@ -37,7 +38,7 @@ from django.utils.text import slugify from django.utils.timezone import is_naive, make_aware from django.utils.safestring import mark_safe -from combo.apps.dashboard.models import DashboardCell +from combo.apps.dashboard.models import DashboardCell, Tile from combo.apps.maps.models import Map, MapLayer from combo.data.models import Page, CellBase, ConfigJsonCell from combo.public.views import render_cell @@ -509,8 +510,29 @@ def as_commune(user_data): @register.assignment_tag -def get_suggestions(request, cell, user_data, places_data): - tile_data = [] +def get_suggestions(request, user_data, places_data): + # fill initial dashboard based on this layout: + ## au quotidien + # mairie tile + # closest velov/tcl/swimming pool/etc. tiles + ## environnement + # air quality + # pollen + + if not getattr(request, 'user', None) or not request.user.is_authenticated(): + # no user + return ['no user'] + + dashboard = DashboardCell.objects.all().filter(page__snapshot__isnull=True)[0] + if Tile.objects.filter(dashboard=dashboard, user=request.user).exists(): + # dashboard already filled + return ['already filled'] + + mairie_tile = None + service_tiles = [] + airquality_tile = {'key': 'airquality', 'parameters': settings.COMBO_MAP_DEFAULT_POSITION} + pollen_tile = {'key': 'pollen'} + addresses = [] city = user_data.get('city') or user_data.get('address_city') if city: @@ -528,42 +550,38 @@ def get_suggestions(request, cell, user_data, places_data): if 'Annexe' in feature['properties']['nom']: continue if city_slug in slugify(feature['properties']['nom']): - tile_data.append({'key': maplayer.slug, - 'properties': feature['properties']}) + mairie_tile = {'key': maplayer.slug, 'properties': feature['properties']} break - if random.random() < 0.3: - tile_data.append({'key': 'airquality'}) - if random.random() < 0.1: - tile_data.append({'key': 'pollen'}) - - if user_data.get('address_street'): + address = None + if places_data and places_data.get('data'): + place_data = places_data['data'][0] + address = u'%(adresse)s, %(ville)s, France' % place_data['content'] + elif user_data.get('address_street'): if not user_data.get('address_number'): user_data['address_number'] = '' - addresses.append(u'%(address_number)s %(address_street)s, %(address_city)s, France' % user_data) + address = u'%(address_number)s %(address_street)s, %(address_city)s, France' % user_data - if places_data: - for place_data in places_data.get('data'): - addresses.append(u'%(adresse)s, %(ville)s, France' % place_data['content']) - - coords = [] - nominatim_url = 'https://nominatim.entrouvert.org' - for address in addresses: + coords = None + if address: + nominatim_url = settings.COMBO_GEOCODING_SERVICE url = '%s/search?q=%s&accept-language=fr&format=json' % ( nominatim_url, quote(address.encode('utf-8'))) try: search_result = requests.get(url, timeout=2, without_user=True, cache_duration=300).json() except RequestException: - continue - if not search_result: - continue - coords.append({'lon': search_result[0]['lon'], 'lat': search_result[0]['lat']}) + pass + if search_result: + coords = {'lon': search_result[0]['lon'], 'lat': search_result[0]['lat']} - for coord in coords: - lat1, lat2 = float(coord['lat']) - 0.008, float(coord['lat']) + 0.008 - lon1, lon2 = float(coord['lon']) - 0.006, float(coord['lon']) + 0.006 - for maplayer in MapLayer.objects.filter(slug__in=('velov', 'piscine', 'tcl')): + if coords: + airquality_tile = {'key': 'airquality', 'parameters': coords} + + lat1, lat2 = float(coords['lat']) - 0.008, float(coords['lat']) + 0.008 + lon1, lon2 = float(coords['lon']) - 0.006, float(coords['lon']) + 0.006 + geod = Geod(ellps='WGS84') + for maplayer in MapLayer.objects.filter(slug__in=('velov', 'piscine', 'tcl', 'bibliotheque', 'mdr')): url = maplayer.geojson_url + '&BBOX=%s,%s,%s,%s' % (lat1, lon1, lat2, lon2) try: data_result = requests.get(url, timeout=2, without_user=True, @@ -574,34 +592,54 @@ def get_suggestions(request, cell, user_data, places_data): if not features: continue for feature in features: - # thanks to the flat earth society - feature['distance'] = math.sqrt( - (float(coord['lon']) - feature['geometry']['coordinates'][0])**2 + - (float(coord['lat']) - feature['geometry']['coordinates'][1])**2) + feature['distance'] = geod.inv( + float(coords['lon']), + float(coords['lat']), + float(feature['geometry']['coordinates'][0]), + float(feature['geometry']['coordinates'][1]))[2] features.sort(key=lambda x: x['distance']) - # take two closest features - for feature in features[:2]: - tile_data.append({'key': maplayer.slug, 'properties': feature['properties']}) + # take closest feature + if features: + service_tiles.append({'key': maplayer.slug, 'properties': features[0]['properties']}) - dashboard = DashboardCell.objects.all()[0] - cells = [] - seen = {} - for data in tile_data: - cell = ConfigJsonCell(key=data['key'], order=1, - page_id=cell.page_id, placeholder='_auto_tile') - cell_form_keys = [x['varname'] for x in settings.JSON_CELL_TYPES[cell.key].get('form') or {}] - cell.parameters = {} - for key in cell_form_keys: - cell.parameters[key] = data['properties'].get(key) - cell_uid = repr((data['key'], cell.parameters)) - if cell_uid in seen: - continue - seen[cell_uid] = True + tiles = [] + if mairie_tile or service_tiles: + tiles.append({'key': 'group-title', 'parameters': {'text': 'Au quotidien'}}) + if mairie_tile: + tiles.append(mairie_tile) + if service_tiles: + random.shuffle(service_tiles) + tiles.extend(service_tiles) + + if airquality_tile or pollen_tile: + tiles.append({'key': 'group-title', 'parameters': {'text': 'Environnement'}}) + if airquality_tile: + tiles.append(airquality_tile) + if pollen_tile: + tiles.append(pollen_tile) + + for i, tile_data in enumerate(tiles): + if 'properties' in tile_data: + cell_form_keys = [x['varname'] for x in settings.JSON_CELL_TYPES[tile_data['key']].get('form') or {}] + tile_data['parameters'] = {} + for key in cell_form_keys: + tile_data['parameters'][key] = tile_data['properties'].get(key) + + cell = ConfigJsonCell( + key=tile_data['key'], + parameters=tile_data.get('parameters', {}), + order=0, + page_id=dashboard.page_id, + placeholder='_suggested_tile') cell.save() - cells.append(render_cell(request, cell=cell).content) + tile = Tile( + dashboard=dashboard, + cell=cell, + user=request.user, + order=i+1) + tile.save() - random.shuffle(cells) - return cells[:5] + return tiles @register.assignment_tag def get_gnm_portal_url(): diff --git a/debian/50gnm.py b/debian/50gnm.py index 0738ce2..26aa57a 100644 --- a/debian/50gnm.py +++ b/debian/50gnm.py @@ -8,6 +8,7 @@ MELLON_ADD_AUTHNREQUEST_NEXT_URL_EXTENSION = True COMBO_MAP_TILE_URLTEMPLATE = "https://tiles.entrouvert.org/hdm/{z}/{x}/{y}.png" COMBO_MAP_ATTRIBUTION = "© OpenStreetMap" +COMBO_GEOCODING_SERVICE = 'https://nominatim.entrouvert.org' COMBO_DASHBOARD_ENABLED = True COMBO_DASHBOARD_NEW_TILE_POSITION = 'first' @@ -248,6 +249,12 @@ JSON_CELL_TYPES = { "name": u"Lieux favoris", "force_async": False, "cache_duration": 600, + "additional-data": [ + { + "key": "userdata", + "url": "[idp_url]api/users/[user_nameid]/" + } + ], "actions": { "create": { "url": "[passerelle_url]jsondatastore/lieux-favoris/data/create?name_id=[user_nameid]" @@ -314,7 +321,8 @@ JSON_CELL_TYPES = { "suggestions": { "name": "Suggestions", "url": "[idp_url]api/users/[user_nameid]/", - "loading-message": "Recherche de suggestions…", + "force_async": True, + "loading-message": "Assemblage en cours, quelques secondes…", "additional-data": [ {"key": "places", "url": "[passerelle_url]jsondatastore/lieux-favoris/data/?name_id=[user_nameid]"