<commit_before>from datetime import datetime
from operator import attrgetter

from django.core.urlresolvers import reverse
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404
from django.http import Http404

from molly.apps.places import get_entity
from molly.apps.places.models import Entity, EntityType, StopOnRoute
from molly.maps import Map
from molly.utils.breadcrumbs import *
from molly.utils.views import BaseView
from molly.wurfl import device_parents
from molly.routing import generate_route, optimise_points
from molly.apps.tours.models import Tour, StopOnTour
from molly.url_shortener import get_shortened_url


class IndexView(BaseView):
    
    @BreadcrumbFactory
    def breadcrumb(self, request, context):
        return Breadcrumb(
            self.conf.local_name,
            None,
            _('Tours'),
            lazy_reverse('index'),
        )
    
    def initial_context(self, request):
        context = super(IndexView, self).initial_context(request)
        
        if 'tours:visited' in request.session:
            context.update({
                'tours': Tour.objects.filter(id__in=request.session['tours:visited'])
            })
        
        return context
    
    def handle_GET(self, request, context):
        return self.render(request, context, 'tours/index')


class CreateView(BaseView):
    
    @BreadcrumbFactory
    def breadcrumb(self, request, context, entities):
        return Breadcrumb(
            self.conf.local_name,
            lazy_parent('index'),
            _('Create a tour'),
            lazy_reverse('create'),
        )
    
    def initial_context(self, request, entities):
        context = super(CreateView, self).initial_context(request)
        
        context.update({
            'entities': [],
            'attractions': dict(
                (et, sorted(et.entity_set.filter(location__isnull=False),
                            key=attrgetter('title')))
                    for et in EntityType.objects.filter(slug__in=self.conf.attraction_types)),
            'all_pois': sorted(Entity.objects.filter(all_types_completion__slug__in=self.conf.attraction_types),
                               key=attrgetter('title'))
        })
        
        for entity in entities.split('/'):
            try:
                scheme, value = entity.split(':')
            except ValueError:
                continue
            context['entities'].append(get_entity(scheme, value))
        
        return context
    
    def handle_GET(self, request, context, entities):
        
        if 'generic_web_browser' in device_parents[request.browser.devid]:
            # Desktop
            return self.render(request, context, 'tours/create_desktop')
        else:
            return self.render(request, context, 'tours/create')


class SaveView(CreateView):
    
    def handle_GET(self, request, context, entities):
        
        if len(context['entities']) < 2:
            # Need at least 2 entities to be a tour
            return self.bad_request(request)
        
        # Now attempt to order entities optimally
        if len(context['entities']) > 2 and len(context['entities']) <= 10:
            context['entities'] = optimise_points(
                [(entity, entity.routing_point().location)
                    for entity in context['entities']])
            context['optimised_entities'] = True
        
        # Come up with a name for this tour
        name = _('Visiting %(number)d places (created on %(creation)s)') % {
                    'number': len(context['entities']),
                    'creation': datetime.now().strftime('%c')
                }
        
        # Save back to database
        tour = Tour.objects.create(name=name)
        for i, entity in enumerate(context['entities']):
            StopOnTour.objects.create(entity=entity, tour=tour, order=i)
        
        # Add any suggested "passing-by" entities to the context to be presented
        # back to the user
        if hasattr(self.conf, 'suggested_entities'):
            route = generate_route([e.location for e in context['entities']], 'foot')
            suggestion_filter = Q()
            for sv in self.conf.suggested_entities:
                scheme, value = sv.split(':')
                suggestion_filter |= Q(_identifiers__scheme=scheme,
                                       _identifiers__value=value)
            context['suggestions'] = Entity.objects.filter(
                suggestion_filter,
                location__distance_lt=(route['path'],<commit_msg>MOLLY-191: Only do suggestions if the PostGIS backend supports it<commit_after>from datetime import datetime
from operator import attrgetter

from django.core.urlresolvers import reverse
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from django.db import connection
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404
from django.http import Http404

from molly.apps.places import get_entity
from molly.apps.places.models import Entity, EntityType, StopOnRoute
from molly.maps import Map
from molly.utils.breadcrumbs import *
from molly.utils.views import BaseView
from molly.wurfl import device_parents
from molly.routing import generate_route, optimise_points
from molly.apps.tours.models import Tour, StopOnTour
from molly.url_shortener import get_shortened_url


class IndexView(BaseView):
    
    @BreadcrumbFactory
    def breadcrumb(self, request, context):
        return Breadcrumb(
            self.conf.local_name,
            None,
            _('Tours'),
            lazy_reverse('index'),
        )
    
    def initial_context(self, request):
        context = super(IndexView, self).initial_context(request)
        
        if 'tours:visited' in request.session:
            context.update({
                'tours': Tour.objects.filter(id__in=request.session['tours:visited'])
            })
        
        return context
    
    def handle_GET(self, request, context):
        return self.render(request, context, 'tours/index')


class CreateView(BaseView):
    
    @BreadcrumbFactory
    def breadcrumb(self, request, context, entities):
        return Breadcrumb(
            self.conf.local_name,
            lazy_parent('index'),
            _('Create a tour'),
            lazy_reverse('create'),
        )
    
    def initial_context(self, request, entities):
        context = super(CreateView, self).initial_context(request)
        
        context.update({
            'entities': [],
            'attractions': dict(
                (et, sorted(et.entity_set.filter(location__isnull=False),
                            key=attrgetter('title')))
                    for et in EntityType.objects.filter(slug__in=self.conf.attraction_types)),
            'all_pois': sorted(Entity.objects.filter(all_types_completion__slug__in=self.conf.attraction_types),
                               key=attrgetter('title'))
        })
        
        for entity in entities.split('/'):
            try:
                scheme, value = entity.split(':')
            except ValueError:
                continue
            context['entities'].append(get_entity(scheme, value))
        
        return context
    
    def handle_GET(self, request, context, entities):
        
        if 'generic_web_browser' in device_parents[request.browser.devid]:
            # Desktop
            return self.render(request, context, 'tours/create_desktop')
        else:
            return self.render(request, context, 'tours/create')


class SaveView(CreateView):
    
    def handle_GET(self, request, context, entities):
        
        if len(context['entities']) < 2:
            # Need at least 2 entities to be a tour
            return self.bad_request(request)
        
        # Now attempt to order entities optimally
        if len(context['entities']) > 2 and len(context['entities']) <= 10:
            context['entities'] = optimise_points(
                [(entity, entity.routing_point().location)
                    for entity in context['entities']])
            context['optimised_entities'] = True
        
        # Come up with a name for this tour
        name = _('Visiting %(number)d places (created on %(creation)s)') % {
                    'number': len(context['entities']),
                    'creation': datetime.now().strftime('%c')
                }
        
        # Save back to database
        tour = Tour.objects.create(name=name)
        for i, entity in enumerate(context['entities']):
            StopOnTour.objects.create(entity=entity, tour=tour, order=i)
        
        # Add any suggested "passing-by" entities to the context to be presented
        # back to the user. We can only do this query if the database backend
        # supports distance operations on geographies (i.e., things more complex
        # than points)
        if hasattr(self.conf, 'suggested_entities') and connection.ops.geography:
            route = generate_route([e.location for e in context['entities']], 'foot')
            suggestion_filter = Q()
            for sv in self.conf.suggested_entities:
                scheme, value = sv.split(':')
                suggestion_filter |= Q(_identifiers__scheme=scheme,
                                       _identifiers__value=value)
            context['suggestions'] = Entity.objects.filter(
                suggestion_filter,
                location__distance_lt=(route['path'],
{'fraction_non_alphanumeric': 0.09543910223411922, 'fraction_numerical': 0.002573870071038814, 'mean_word_length': 2.2315369261477045, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7428689', 'n_tokens_mistral': 2621, 'n_tokens_neox': 2569, 'n_words': 635}