combo-plugin-gnm/combo_plugin_gnm/templatetags/gnm.py

287 lines
10 KiB
Python

# -*- coding: utf-8 -*-
# combo-plugin-gnm - Combo GNM plugin
# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>.
import datetime
import random
import re
import urllib2
from requests import RequestException
from django import template
from django.conf import settings
from django.utils.safestring import mark_safe
from django.utils.text import slugify
from combo.apps.dashboard.models import DashboardCell
from combo.apps.maps.models import MapLayer
from combo.data.models import Page, ConfigJsonCell
from combo.public.views import render_cell
from combo.utils import requests
register = template.Library()
class TimeSlot(object):
def __init__(self, start, end):
self.start = start
self.end = end
@register.filter
def as_opening_hours_badge(data):
if not data:
return ''
weekdays = ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche']
slots = []
today = datetime.date.today()
for i in range(7):
for period in ('am', 'pm'):
hours = data.get('%s_%s' % (weekdays[today.weekday()], period))
if not hours:
continue
try:
parts = re.match('(\d?\d)h(\d\d)-(\d?\d)h(\d\d)', hours).groups()
except AttributeError:
continue
slots.append(TimeSlot(
datetime.datetime(today.year, today.month, today.day, int(parts[0]), int(parts[1])),
datetime.datetime(today.year, today.month, today.day, int(parts[2]), int(parts[3]))))
today = today + datetime.timedelta(days=1)
now = datetime.datetime.now()
# remove past slots
for i, slot in enumerate(slots):
if now > slot.end:
slots[i] = None
slots = [x for x in slots if x]
if not slots:
klass = 'closed'
label = u'Fermé'
elif now < slots[0].start:
klass = 'closed'
verb = u'Réouvre'
if slots[0].start.weekday() == today.weekday():
day_label = ''
if slots[0].start.hour < 12:
verb = 'Ouvre'
elif slots[0].start.weekday() == (today.weekday() + 1) % 7:
day_label = u'demain'
else:
day_label = weekdays[slots[0].start.weekday()]
label = u'%s %s à %sh%02d' % (verb, day_label, slots[0].start.hour, slots[0].start.minute)
elif now < slots[0].end:
if (slots[0].end - now).seconds < 3600:
klass = 'soon-to-be-closed'
else:
klass = 'open'
label = u"Ouvert jusqu'à %sh%02d" % (slots[0].end.hour, slots[0].end.minute)
return mark_safe('<div class="badge %s"><span>%s</span></div>' % (klass, label))
@register.filter
def onlymoov_duration(string):
# take the hours and minutes components of duration strings provided by
# onlymoov, "PT1H16M3S", "PT1M35S"
try:
groups = re.match(r'PT(\d+H)?(\d+M)', string).groups()
except AttributeError: # didn't match
return '?'
hours = ''
if groups[0]:
nb_hours = int(groups[0][:-1])
if nb_hours > 1:
hours = '%s heures' % nb_hours
else:
hours = '%s heure' % nb_hours
nb_minutes = int(groups[1][:-1])
if nb_minutes == 0:
minutes = ''
else:
minutes = '%d min' % nb_minutes
return '%s %s' % (hours, minutes)
@register.filter
def place_page_url(cell):
try:
fixed_place_cell = ConfigJsonCell.objects.get(
key=cell.key,
parameters=cell.parameters,
page__template_name='place')
except ConfigJsonCell.DoesNotExist:
return ''
return fixed_place_cell.page.get_online_url()
@register.filter
def is_place_page(page):
if not page:
return False
return page.template_name == 'place'
@register.filter
def as_producer(slug):
if ':' in slug: # formdef_reference
slug = slug.split(':')[0]
if slug.startswith('_'):
collectivity = slug.split('_')[1]
return {'slug': collectivity,
'label': settings.KNOWN_SERVICES['hobo'].get('hobo-%s' % collectivity, {'title': ''})['title']}
return {'slug': 'grandlyon', 'label': 'Grand Lyon'}
@register.filter
def as_commune(user_data):
if not user_data:
return None
city = user_data.get('city') or user_data.get('address_city')
if city:
# first look for known portals
collectivities = get_gnm_collectivities()
for collectivity in collectivities:
if collectivity.get('label') == city:
return {
'label': city,
'slug': slugify(city),
'url': collectivity['url']
}
# if not found look in mairie pages
pages = Page.objects.filter(parent__slug='mairie',
slug__icontains=slugify(city)).exclude(slug__icontains='annexe')
if pages.exists():
return {
'label': city,
'slug': slugify(city),
'url': pages[0].get_online_url(),
}
return None
@register.assignment_tag
def get_suggestions(request, cell, user_data, places_data):
tile_data = []
addresses = []
city = user_data.get('city') or user_data.get('address_city')
if city:
# get commune tile for the user city
maplayer = MapLayer.objects.get(slug='mairie')
try:
data_result = requests.get(maplayer.geojson_url, timeout=2,
without_user=True, cache_duration=300).json()
except RequestException:
pass
else:
city_slug = slugify(city)
if data_result.get('features'):
for feature in data_result['features']:
if 'Annexe' in feature['properties']['nom']:
continue
if city_slug in slugify(feature['properties']['nom']):
tile_data.append({'key': maplayer.slug,
'properties': feature['properties']})
break
if random.random() < 0.6:
tile_data.append({'key': 'airquality'})
if random.random() < 0.3:
tile_data.append({'key': 'pollen'})
if 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)
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:
url = '%s/search?q=%s&accept-language=fr&format=json' % (
nominatim_url, urllib2.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']})
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')):
url = maplayer.geojson_url + '&BBOX=%s,%s,%s,%s' % (lat1, lon1, lat2, lon2)
try:
data_result = requests.get(url, timeout=2, without_user=True,
cache_duration=300).json()
except RequestException:
continue
if not data_result.get('features'):
continue
for feature in random.sample(
data_result.get('features'), min(len(data_result.get('features')), 2)):
tile_data.append({'key': maplayer.slug,
'properties': feature['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
cell.save()
cells.append(render_cell(request, cell=cell).content)
random.shuffle(cells)
return cells[:5]
@register.assignment_tag
def get_gnm_portal_url():
if '_interco_portal' in settings.KNOWN_SERVICES['combo']:
return settings.KNOWN_SERVICES['combo']['_interco_portal'].get('url')
return settings.KNOWN_SERVICES['combo']['portal'].get('url')
@register.assignment_tag
def get_gnm_collectivities():
collectivities = []
for key in settings.KNOWN_SERVICES['combo']:
if not key.endswith('_portal'):
continue
matching_hobo = settings.KNOWN_SERVICES['hobo'].get(key.split('_portal')[0][1:])
if not matching_hobo:
continue
if matching_hobo['title'] in ('SAU'): # blacklist
continue
service = settings.KNOWN_SERVICES['combo'][key]
collectivities.append({'url': service.get('url'), 'label': matching_hobo['title']})
collectivities.sort(key=lambda x: x['label'])
return collectivities