Compare commits
	
		
			15 Commits
		
	
	
		
			update/dja
			...
			develop
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3982db1375 | ||
| 
						 | 
					e60a6ea9be | ||
| 
						 | 
					bc0ace7bf3 | ||
| 
						 | 
					df67bcf639 | ||
| 
						 | 
					724c26c926 | ||
| 
						 | 
					06d68380c9 | ||
| 
						 | 
					f06b6bdae9 | ||
| 
						 | 
					7a7c06882a | ||
| 
						 | 
					c9d83dfc2c | ||
| 
						 | 
					49301afe51 | ||
| 
						 | 
					624878624f | ||
| 
						 | 
					a2ee323fa4 | ||
| 
						 | 
					86c9de3213 | ||
| 
						 | 
					8597e53599 | ||
| 
						 | 
					d213b51a59 | 
							
								
								
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							@@ -22,6 +22,7 @@ easy-thumbnails = "*"
 | 
				
			|||||||
image = "*"
 | 
					image = "*"
 | 
				
			||||||
django-widget-tweaks = "*"
 | 
					django-widget-tweaks = "*"
 | 
				
			||||||
django-taggit = "*"
 | 
					django-taggit = "*"
 | 
				
			||||||
 | 
					pykml = "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[scripts]
 | 
					[scripts]
 | 
				
			||||||
test = "django_lostplaces/manage.py test lostplaces"
 | 
					test = "django_lostplaces/manage.py test lostplaces"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
# lostplaces-backend
 | 
					# lostplaces-backend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lostplaces-backend is a django (3.x) based webproject. It once wants to become a software which allows a group of urban explorers to manage, document and share the locations of lost places while not exposing too much / any information to the public.
 | 
					lostplaces-backend is a django (4.x) based webproject. It once wants to become a software which allows a group of urban explorers to manage, document and share the locations of lost places while not exposing too much / any information to the public.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The software is currently in early development status, neither scope, datamodel(s) nor features are finalized yet. Therefore we would not recommend to download or install this piece of software anywhere - except your local django dev server.
 | 
					The software is currently in early development status, neither scope, datamodel(s) nor features are finalized yet. Therefore we would not recommend to download or install this piece of software anywhere - except your local django dev server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,7 +69,7 @@ class PlaceForm(forms.ModelForm):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = Place
 | 
					        model = Place
 | 
				
			||||||
        fields = '__all__'
 | 
					        fields = '__all__'
 | 
				
			||||||
        exclude = ['submitted_by', 'level']
 | 
					        exclude = ['submitted_by', 'level', 'mode']
 | 
				
			||||||
        widgets = {
 | 
					        widgets = {
 | 
				
			||||||
            'hero': widgets.SelectContent()
 | 
					            'hero': widgets.SelectContent()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -88,6 +88,11 @@ class PlaceForm(forms.ModelForm):
 | 
				
			|||||||
        widget=forms.NumberInput(attrs={'min':-180,'max': 180,'type': 'number', 'step': 'any'})
 | 
					        widget=forms.NumberInput(attrs={'min':-180,'max': 180,'type': 'number', 'step': 'any'})
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    draft = forms.BooleanField(
 | 
				
			||||||
 | 
					        label=_('Save Place as draft'),
 | 
				
			||||||
 | 
					        required=False
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlaceImageForm(forms.ModelForm):
 | 
					class PlaceImageForm(forms.ModelForm):
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = PlaceImage
 | 
					        model = PlaceImage
 | 
				
			||||||
@@ -122,3 +127,9 @@ class TagSubmitForm(forms.Form):
 | 
				
			|||||||
        required=False, 
 | 
					        required=False, 
 | 
				
			||||||
        widget=forms.TextInput(attrs={'autocomplete':'off'})
 | 
					        widget=forms.TextInput(attrs={'autocomplete':'off'})
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UploadMapFileForm(forms.Form):
 | 
				
			||||||
 | 
					    map_file = forms.FileField()
 | 
				
			||||||
 | 
					    description = forms.CharField(
 | 
				
			||||||
 | 
					        widget=forms.Textarea
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,9 +82,25 @@ class Explorer(models.Model):
 | 
				
			|||||||
        choices=EXPLORER_LEVELS
 | 
					        choices=EXPLORER_LEVELS
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_place_list_to_display(self):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Gets the list of places to show on the homepage
 | 
				
			||||||
 | 
					        and the list views
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        if self.user.is_superuser:
 | 
				
			||||||
 | 
					            return Place.objects.filter(mode='live').order_by('submitted_when')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (Place.objects.filter(
 | 
				
			||||||
 | 
					            level__lte=self.level,
 | 
				
			||||||
 | 
					            mode='live'
 | 
				
			||||||
 | 
					        ) | Place.objects.filter(
 | 
				
			||||||
 | 
					            submitted_by=self,
 | 
				
			||||||
 | 
					            mode='live'
 | 
				
			||||||
 | 
					        )).order_by('submitted_when')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_places_eligible_to_see(self):
 | 
					    def get_places_eligible_to_see(self):
 | 
				
			||||||
        if self.user.is_superuser:
 | 
					        if self.user.is_superuser:
 | 
				
			||||||
            return Place.objects.all()
 | 
					            return Place.objects.filter(mode='live')
 | 
				
			||||||
        return Place.objects.all().filter(level__lte=self.level) | self.places.all()
 | 
					        return Place.objects.all().filter(level__lte=self.level) | self.places.all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_eligible_to_see(self, place):
 | 
					    def is_eligible_to_see(self, place):
 | 
				
			||||||
@@ -94,6 +110,12 @@ class Explorer(models.Model):
 | 
				
			|||||||
            place in self.get_places_eligible_to_see()
 | 
					            place in self.get_places_eligible_to_see()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_drafts(self):
 | 
				
			||||||
 | 
					        return Place.objects.filter(
 | 
				
			||||||
 | 
					            submitted_by=self,
 | 
				
			||||||
 | 
					            mode='draft'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return self.user.username
 | 
					        return self.user.username
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,13 @@
 | 
				
			|||||||
import os
 | 
					import os
 | 
				
			||||||
from math import floor
 | 
					import datetime
 | 
				
			||||||
 | 
					from math import ceil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
from django.dispatch import receiver
 | 
					from django.dispatch import receiver
 | 
				
			||||||
from django.db.models.signals import post_delete, pre_save
 | 
					from django.db.models.signals import post_delete, pre_save
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces.models.abstract_models import Submittable, Taggable, Mapable, Expireable
 | 
					from lostplaces.models.abstract_models import Submittable, Taggable, Mapable, Expireable
 | 
				
			||||||
@@ -21,6 +23,43 @@ PLACE_LEVELS = (
 | 
				
			|||||||
    (5, 'Time Capsule')
 | 
					    (5, 'Time Capsule')
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PLACE_MODES = (
 | 
				
			||||||
 | 
					    ('live', 'live'),
 | 
				
			||||||
 | 
					    ('draft', 'draft'),
 | 
				
			||||||
 | 
					    ('review', 'review'),
 | 
				
			||||||
 | 
					    ('archive', 'archive'),
 | 
				
			||||||
 | 
					    ('imported', 'imported')
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PLACE_IMPORT_TYPES = (
 | 
				
			||||||
 | 
					    ('kml', 'KML-File import'),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PlaceImport(models.Model):
 | 
				
			||||||
 | 
					    imported_when = models.DateTimeField(
 | 
				
			||||||
 | 
					        auto_now_add=True,
 | 
				
			||||||
 | 
					        verbose_name=_('When the imported has taken place')
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    description = models.TextField(
 | 
				
			||||||
 | 
					        default=None,
 | 
				
			||||||
 | 
					        null=True,
 | 
				
			||||||
 | 
					        verbose_name=_('Description of the import')
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    explorer = models.ForeignKey(
 | 
				
			||||||
 | 
					        'Explorer',
 | 
				
			||||||
 | 
					        null=True,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        related_name='place_imports'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import_type = models.TextField(
 | 
				
			||||||
 | 
					        default='kml',
 | 
				
			||||||
 | 
					        choices=PLACE_IMPORT_TYPES,
 | 
				
			||||||
 | 
					        verbose_name=_('What kind of import this is')
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Place(Submittable, Taggable, Mapable):
 | 
					class Place(Submittable, Taggable, Mapable):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Place defines a lost place (location, name, description etc.).
 | 
					    Place defines a lost place (location, name, description etc.).
 | 
				
			||||||
@@ -48,6 +87,22 @@ class Place(Submittable, Taggable, Mapable):
 | 
				
			|||||||
        choices=PLACE_LEVELS
 | 
					        choices=PLACE_LEVELS
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mode = models.TextField(
 | 
				
			||||||
 | 
					        default='live',
 | 
				
			||||||
 | 
					        choices=PLACE_MODES,
 | 
				
			||||||
 | 
					        verbose_name=_('Mode of Place Editing')
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    place_import = models.ForeignKey(
 | 
				
			||||||
 | 
					        PlaceImport,
 | 
				
			||||||
 | 
					        null=True,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        related_name='place_list'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_imported(self):
 | 
				
			||||||
 | 
					        return self.place_import != None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_hero_image(self):
 | 
					    def get_hero_image(self):
 | 
				
			||||||
        if self.hero:
 | 
					        if self.hero:
 | 
				
			||||||
            return self.hero
 | 
					            return self.hero
 | 
				
			||||||
@@ -76,24 +131,22 @@ class Place(Submittable, Taggable, Mapable):
 | 
				
			|||||||
    # Get center position of LP-geocoordinates.
 | 
					    # Get center position of LP-geocoordinates.
 | 
				
			||||||
    def average_latlon(cls, place_list):
 | 
					    def average_latlon(cls, place_list):
 | 
				
			||||||
        amount = len(place_list)
 | 
					        amount = len(place_list)
 | 
				
			||||||
        # Init fill values to prevent None
 | 
					 | 
				
			||||||
        # China Corner in Münster
 | 
					 | 
				
			||||||
        # Where I almost always eat lunch
 | 
					 | 
				
			||||||
        # (Does'nt help losing wheight, tho)
 | 
					 | 
				
			||||||
        longitude = 7.6295628132604385
 | 
					 | 
				
			||||||
        latitude = 51.961922091398904
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if amount > 0:
 | 
					        if amount > 0:
 | 
				
			||||||
 | 
					            latitude = 0
 | 
				
			||||||
 | 
					            longitude = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for place in place_list:
 | 
					            for place in place_list:
 | 
				
			||||||
                longitude += place.longitude
 | 
					                longitude += place.longitude
 | 
				
			||||||
                latitude += place.latitude
 | 
					                latitude += place.latitude
 | 
				
			||||||
            return {'latitude':latitude / amount, 'longitude': longitude / amount}
 | 
					            return {'latitude': latitude / amount, 'longitude': longitude / amount}
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
        return {'latitude': latitude, 'longitude': longitude}
 | 
					            # Location of China Corner in Münster
 | 
				
			||||||
 | 
					            # Where I almost always eat lunch
 | 
				
			||||||
 | 
					            # (Does'nt help losing wheight, tho)
 | 
				
			||||||
 | 
					            return {'latitude': 51.961922091398904, 'longitude': 7.6295628132604385}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def calculate_place_level(self):
 | 
					    def calculate_place_level(self):
 | 
				
			||||||
        self.remove_expired_votes()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.placevotings.count() == 0:
 | 
					        if self.placevotings.count() == 0:
 | 
				
			||||||
            self.level = 5
 | 
					            self.level = 5
 | 
				
			||||||
            self.save()
 | 
					            self.save()
 | 
				
			||||||
@@ -104,13 +157,22 @@ class Place(Submittable, Taggable, Mapable):
 | 
				
			|||||||
        for vote in self.placevotings.all():
 | 
					        for vote in self.placevotings.all():
 | 
				
			||||||
            level += vote.vote
 | 
					            level += vote.vote
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.level = floor(level / self.placevotings.count())
 | 
					        self.level = round(level / self.placevotings.count())
 | 
				
			||||||
        self.save()
 | 
					        self.save()
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    def remove_expired_votes(self):
 | 
					    def calculate_voting_accuracy(self):
 | 
				
			||||||
 | 
					        place_age = timezone.now() - self.submitted_when;
 | 
				
			||||||
 | 
					        accuaries = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for vote in self.placevotings.all():
 | 
					        for vote in self.placevotings.all():
 | 
				
			||||||
            if vote.is_expired:
 | 
					            vote_age = timezone.now() - vote.submitted_when;
 | 
				
			||||||
                vote.delete()
 | 
					            accuracy = 100 - (100 / (place_age / vote_age))
 | 
				
			||||||
 | 
					            accuaries.append(accuracy)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if len(accuaries) > 0:
 | 
				
			||||||
 | 
					            return ceil(sum(accuaries) / len(accuaries))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return self.name
 | 
					        return self.name
 | 
				
			||||||
@@ -210,7 +272,7 @@ def auto_delete_file_on_change(sender, instance, **kwargs):
 | 
				
			|||||||
        old_file.delete(save=False)
 | 
					        old_file.delete(save=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlaceVoting(PlaceAsset, Expireable):
 | 
					class PlaceVoting(PlaceAsset):
 | 
				
			||||||
    vote = models.IntegerField(choices=PLACE_LEVELS)
 | 
					    vote = models.IntegerField(choices=PLACE_LEVELS)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def get_human_readable_level(self):
 | 
					    def get_human_readable_level(self):
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								django_lostplaces/lostplaces/templates/explorer/drafts.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								django_lostplaces/lostplaces/templates/explorer/drafts.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					{% extends 'global.html'%}
 | 
				
			||||||
 | 
					{% load static %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load svg_icon %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block maincontent %}
 | 
				
			||||||
 | 
					<section class="LP-Section">
 | 
				
			||||||
 | 
						<div class="LP-PlaceList">
 | 
				
			||||||
 | 
							<h1 class="LP-Headline">
 | 
				
			||||||
 | 
					            {% if user.username == explorer.user.username %}
 | 
				
			||||||
 | 
					                {% translate 'Your drafts' %}
 | 
				
			||||||
 | 
					            {% else %}
 | 
				
			||||||
 | 
					                {{explorer.user.username}}{% translate '\'s drafts' %}
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					        </h1>
 | 
				
			||||||
 | 
							<ul class="LP-PlaceList__List">
 | 
				
			||||||
 | 
								{% for place in place_list %}
 | 
				
			||||||
 | 
								<li class="LP-PlaceList__Item">
 | 
				
			||||||
 | 
									<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
 | 
				
			||||||
 | 
										{% include 'partials/place_teaser.html' with place=place extended=True %}
 | 
				
			||||||
 | 
									</a>
 | 
				
			||||||
 | 
								</li>
 | 
				
			||||||
 | 
								{% endfor %}
 | 
				
			||||||
 | 
							</ul>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
 | 
					{% endblock maincontent %}
 | 
				
			||||||
@@ -32,7 +32,8 @@
 | 
				
			|||||||
                    {% if user.is_authenticated %}
 | 
					                    {% if user.is_authenticated %}
 | 
				
			||||||
                    Hi {{ user.username }}!
 | 
					                    Hi {{ user.username }}!
 | 
				
			||||||
                    <a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% translate 'Logout' %}</span></a> |
 | 
					                    <a class="LP-Link" href="{% url 'logout' %}"><span class="LP-Link__Text">{% translate 'Logout' %}</span></a> |
 | 
				
			||||||
					<a class="LP-Link" href="{% url 'explorer_profile' explorer_id=user.pk%}"><span class="LP-Link__Text">{% translate 'Profile' %}</span></a>
 | 
										<a class="LP-Link" href="{% url 'explorer_profile' explorer_id=user.pk%}"><span class="LP-Link__Text">{% translate 'Profile' %}</span></a> |
 | 
				
			||||||
 | 
					                    <a class="LP-Link" href="{% url 'explorer_drafts' explorer_id=user.pk%}"><span class="LP-Link__Text">{% translate 'Drafts' %}</span></a>
 | 
				
			||||||
                    {% if user.is_superuser %}
 | 
					                    {% if user.is_superuser %}
 | 
				
			||||||
                    | <a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% translate 'Admin' %}</span></a>
 | 
					                    | <a class="LP-Link" href="{% url 'admin:index' %}" target="_blank"><span class="LP-Link__Text">{% translate 'Admin' %}</span></a>
 | 
				
			||||||
                    {% endif %}
 | 
					                    {% endif %}
 | 
				
			||||||
@@ -58,6 +59,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    <li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_create'%}" class="LP-Link"><span class="LP-Link__Text">{% translate 'Create place' %}</span></a></li>
 | 
					                    <li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_create'%}" class="LP-Link"><span class="LP-Link__Text">{% translate 'Create place' %}</span></a></li>
 | 
				
			||||||
                    <li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_list'%}" class="LP-Link"><span class="LP-Link__Text">{% translate 'All places' %}</span></a></li>
 | 
					                    <li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'place_list'%}" class="LP-Link"><span class="LP-Link__Text">{% translate 'All places' %}</span></a></li>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    {% if user.is_superuser %}
 | 
				
			||||||
 | 
					                    <li class="LP-Menu__Item LP-Menu__Item--additional"><a href="{% url 'import_upload'%}" class="LP-Link"><span class="LP-Link__Text">{% translate 'Import KML File' %}</span></a></li>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
                </ul>
 | 
					                </ul>
 | 
				
			||||||
            </nav>
 | 
					            </nav>
 | 
				
			||||||
        </aside>
 | 
					        </aside>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					{% extends 'global.html'%}
 | 
				
			||||||
 | 
					{% load static %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load svg_icon %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block maincontent %}
 | 
				
			||||||
 | 
					<h1 class="LP-Headline">
 | 
				
			||||||
 | 
					    {{ import_type}} from {{import.imported_when|date:"d F Y"}} by {{import.explorer.user.username}}
 | 
				
			||||||
 | 
					</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<p class="SP-Paragraph">
 | 
				
			||||||
 | 
					    {{import.place_list.all|length}} places where import in this import
 | 
				
			||||||
 | 
					</p>
 | 
				
			||||||
 | 
					<p class="SP-Paragraph">
 | 
				
			||||||
 | 
					    {{import.description}}
 | 
				
			||||||
 | 
					</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="LP-PlaceList">
 | 
				
			||||||
 | 
					    <h1 class="LP-Headline">{% translate 'Places imported' %}</h1>
 | 
				
			||||||
 | 
					    <ul class="LP-PlaceList__List">
 | 
				
			||||||
 | 
					        {% for place in paginated_places %}
 | 
				
			||||||
 | 
					        <li class="LP-PlaceList__Item">
 | 
				
			||||||
 | 
					            {% include 'partials/place_teaser.html' with place=place extended=True %}
 | 
				
			||||||
 | 
					        </li>
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					    </ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% include 'partials/nav/pagination.html' with page_obj=paginated_places is_paginated=True %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock maincontent %}
 | 
				
			||||||
@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					{% extends 'global.html'%}
 | 
				
			||||||
 | 
					{% load static %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load svg_icon %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block maincontent %}
 | 
				
			||||||
 | 
					<form class="LP-Form" method="POST" enctype="multipart/form-data">
 | 
				
			||||||
 | 
					    <fieldset class="LP-Form__Fieldset">
 | 
				
			||||||
 | 
					        <legend class="LP-Form__Legend">{% translate 'Import Places from KML File' %}</legend>
 | 
				
			||||||
 | 
					        {% csrf_token %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="LP-Form__Composition LP-Form__Composition--breakable">
 | 
				
			||||||
 | 
					            <div class="LP-Form__Field">
 | 
				
			||||||
 | 
					                {% include 'partials/form/inputField.html' with field=upload_form.description %}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="LP-Form__Composition LP-Form__Composition--breakable">
 | 
				
			||||||
 | 
					            <div class="LP-Form__Field">
 | 
				
			||||||
 | 
					                {% include 'partials/form/inputField.html' with field=upload_form.map_file %}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {% translate 'Create' as action %}
 | 
				
			||||||
 | 
					        <div class="LP-Form__Composition LP-Form__Composition--buttons">
 | 
				
			||||||
 | 
					            {% include 'partials/form/submit.html' with referrer=request.META.HTTP_REFERER action=action %}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
 | 
					{% endblock maincontent %}
 | 
				
			||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
    <div class="LP-PlaceTeaser__Meta">
 | 
					    <div class="LP-PlaceTeaser__Meta">
 | 
				
			||||||
        <div class="LP-PlaceTeaser__Info">
 | 
					        <div class="LP-PlaceTeaser__Info">
 | 
				
			||||||
            <span class="LP-PlaceTeaser__Title">
 | 
					            <span class="LP-PlaceTeaser__Title">
 | 
				
			||||||
                <h1 class="LP-Headline LP-Headline--teaser">{{place.name|truncatechars:19}}</h1>
 | 
					                <h1 class="LP-Headline LP-Headline--teaser">{{place.name}}</h1>
 | 
				
			||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
            <span class="LP-PlaceTeaser__Detail">
 | 
					            <span class="LP-PlaceTeaser__Detail">
 | 
				
			||||||
                <p class="LP-Paragraph">{{place.location|truncatechars:25}}</p>
 | 
					                <p class="LP-Paragraph">{{place.location|truncatechars:25}}</p>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,12 +28,7 @@
 | 
				
			|||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<div class="LP-Voting__Expiration">
 | 
									<div class="LP-Voting__Expiration">
 | 
				
			||||||
					<span class="LP-Voting__InfoLabel">Your vote expires on</span>
 | 
										The accuracy of the voting is {{voting.accuracy}}%
 | 
				
			||||||
					<span class="LP-Voting__Date">
 | 
					 | 
				
			||||||
						<time datetime="{{voting.expires_when|date:'Y-m-d'}}">
 | 
					 | 
				
			||||||
							{{voting.users_vote.expires_when|date:'d.m.Y'}}
 | 
					 | 
				
			||||||
						</time>
 | 
					 | 
				
			||||||
					</span>
 | 
					 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,8 +39,10 @@
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {% translate 'Create' as action %}
 | 
					        {% translate 'Create' as action %}
 | 
				
			||||||
        <div class="LP-Form__Composition LP-Form__Composition--buttons">
 | 
					        <div class="LP-Form__Composition LP-Form__Composition--buttons">
 | 
				
			||||||
 | 
					            {% include 'partials/form/inputField.html' with field=place_form.draft %}
 | 
				
			||||||
            {% include 'partials/form/submit.html' with referrer=request.META.HTTP_REFERER action=action %}
 | 
					            {% include 'partials/form/submit.html' with referrer=request.META.HTTP_REFERER action=action %}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </fieldset>
 | 
					    </fieldset>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,15 @@
 | 
				
			|||||||
{% block maincontent %}
 | 
					{% block maincontent %}
 | 
				
			||||||
<article class="LP-PlaceDetail">
 | 
					<article class="LP-PlaceDetail">
 | 
				
			||||||
    <header class="LP-PlaceDetail__Header">
 | 
					    <header class="LP-PlaceDetail__Header">
 | 
				
			||||||
        <h1 class="LP-Headline">{{ place.name }} {% include 'partials/icons/place_favorite.html' %} {% include 'partials/icons/place_visited.html' %}</h1>
 | 
					        <h1 class="LP-Headline">
 | 
				
			||||||
 | 
								{{ place.name }}
 | 
				
			||||||
 | 
								{% include 'partials/icons/place_favorite.html' %}
 | 
				
			||||||
 | 
								{% include 'partials/icons/place_visited.html' %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								{% if user.is_superuser %}
 | 
				
			||||||
 | 
					            	<a class="LP-Link" href="{{'/admin/lostplaces/place/'|addstr:place.id }}" target="_blank"><span class="LP-Link__Text">{% translate 'view place in admin panel' %}</span></a>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
							</h1>
 | 
				
			||||||
        {% if place.get_hero_image %}
 | 
					        {% if place.get_hero_image %}
 | 
				
			||||||
        <div class="LP-PlaceDetail__Image">
 | 
					        <div class="LP-PlaceDetail__Image">
 | 
				
			||||||
			{% include '../partials/image.html' with source_url=place.get_hero_image.filename.hero.url link_url="#image"|addstr:place.get_hero_index_in_queryset %}
 | 
								{% include '../partials/image.html' with source_url=place.get_hero_image.filename.hero.url link_url="#image"|addstr:place.get_hero_index_in_queryset %}
 | 
				
			||||||
@@ -41,7 +49,6 @@
 | 
				
			|||||||
			{% include '../partials/tagging.html' with config=tagging_config %}
 | 
								{% include '../partials/tagging.html' with config=tagging_config %}
 | 
				
			||||||
		</section>
 | 
							</section>
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		{{votingplace.vote}}
 | 
					 | 
				
			||||||
		<section class="LP-Section">
 | 
							<section class="LP-Section">
 | 
				
			||||||
			{% include '../partials/voting.html' with voting=placevoting %}
 | 
								{% include '../partials/voting.html' with voting=placevoting %}
 | 
				
			||||||
		</section>
 | 
							</section>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="LP-Form__Composition LP-Form__Composition--buttons">
 | 
					        <div class="LP-Form__Composition LP-Form__Composition--buttons">
 | 
				
			||||||
            {% include 'partials/form/submit.html' with referrer=request.META.HTTP_REFERER %}
 | 
					            {% include 'partials/form/submit.html' with referer=request.META.HTTP_REFERER %}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </fieldset>
 | 
					    </fieldset>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,21 +42,25 @@ class PlaceImageTestCase(ModelTestCase):
 | 
				
			|||||||
        if not os.path.isdir(settings.MEDIA_ROOT):
 | 
					        if not os.path.isdir(settings.MEDIA_ROOT):
 | 
				
			||||||
            os.mkdir(settings.MEDIA_ROOT)
 | 
					            os.mkdir(settings.MEDIA_ROOT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        images_dir = os.path.join(
 | 
				
			||||||
 | 
					            settings.MEDIA_ROOT, 
 | 
				
			||||||
 | 
					            settings.RELATIVE_THUMBNAIL_PATH,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        current_dir = os.path.dirname(os.path.abspath(__file__))
 | 
					        current_dir = os.path.dirname(os.path.abspath(__file__))
 | 
				
			||||||
        if not os.path.isfile(os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg')):
 | 
					        if not os.path.isfile(os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg')):
 | 
				
			||||||
            shutil.copyfile(
 | 
					            shutil.copyfile(
 | 
				
			||||||
                os.path.join(current_dir, 'im_a_image.jpeg'),
 | 
					                os.path.join(current_dir, 'im_a_image.jpeg'),
 | 
				
			||||||
                os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg')
 | 
					                os.path.join(images_dir, 'im_a_image_copy.jpeg')
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            shutil.copyfile(
 | 
					            shutil.copyfile(
 | 
				
			||||||
                os.path.join(current_dir, 'im_a_image.jpeg'),
 | 
					                os.path.join(current_dir, 'im_a_image.jpeg'),
 | 
				
			||||||
                os.path.join(settings.MEDIA_ROOT, 'im_a_image_changed.jpeg')
 | 
					                os.path.join(images_dir, 'im_a_image_changed.jpeg')
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        PlaceImage.objects.create(
 | 
					        PlaceImage.objects.create(
 | 
				
			||||||
            description='Im a description',
 | 
					            description='Im a description',
 | 
				
			||||||
            filename=os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg'),
 | 
					            filename=os.path.join(images_dir, 'im_a_image_copy.jpeg'),
 | 
				
			||||||
            place=place,
 | 
					            place=place,
 | 
				
			||||||
            submitted_when=timezone.now(),
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
            submitted_by=user.explorer
 | 
					            submitted_by=user.explorer
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,14 @@
 | 
				
			|||||||
#!/usr/bin/env python
 | 
					#!/usr/bin/env python
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import datetime
 | 
				
			||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib.auth.models import User
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces.models import Place
 | 
					from lostplaces.models import Place, PlaceVoting
 | 
				
			||||||
from lostplaces.tests.models import ModelTestCase
 | 
					from lostplaces.tests.models import ModelTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlaceTestCase(ModelTestCase):
 | 
					class PlaceTestCase(ModelTestCase):
 | 
				
			||||||
@@ -106,12 +107,12 @@ class PlaceTestCase(ModelTestCase):
 | 
				
			|||||||
        an empty list
 | 
					        an empty list
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        avg_latlon = Place.average_latlon([])
 | 
					        avg_latlon = Place.average_latlon([])
 | 
				
			||||||
        self.assertEqual(avg_latlon['latitude'], 0,
 | 
					        self.assertEqual(avg_latlon['latitude'], 51.961922091398904,
 | 
				
			||||||
            msg='%s: (no places) average latitude missmatch' % (
 | 
					            msg='%s: (no places) average latitude missmatch' % (
 | 
				
			||||||
                self.model.__name__
 | 
					                self.model.__name__
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(avg_latlon['longitude'], 0,
 | 
					        self.assertEqual(avg_latlon['longitude'], 7.6295628132604385,
 | 
				
			||||||
            msg='%s: (no places) average longitude missmatch' % (
 | 
					            msg='%s: (no places) average longitude missmatch' % (
 | 
				
			||||||
                self.model.__name__
 | 
					                self.model.__name__
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
@@ -124,3 +125,169 @@ class PlaceTestCase(ModelTestCase):
 | 
				
			|||||||
                self.model.__name__
 | 
					                self.model.__name__
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_level_calculation(self):
 | 
				
			||||||
 | 
					        explorer = self.place.submitted_by
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PlaceVoting.objects.create(
 | 
				
			||||||
 | 
					            submitted_by=explorer,
 | 
				
			||||||
 | 
					            place=self.place,
 | 
				
			||||||
 | 
					            vote=5
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        PlaceVoting.objects.create(
 | 
				
			||||||
 | 
					            submitted_by=explorer,
 | 
				
			||||||
 | 
					            place=self.place,
 | 
				
			||||||
 | 
					            vote=2
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        PlaceVoting.objects.create(
 | 
				
			||||||
 | 
					            submitted_by=explorer,
 | 
				
			||||||
 | 
					            place=self.place,
 | 
				
			||||||
 | 
					            vote=4
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.place.calculate_place_level()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            4,
 | 
				
			||||||
 | 
					            self.place.level,
 | 
				
			||||||
 | 
					            msg='Expecting the place level to be 4'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_level_calculation_no_votes(self):
 | 
				
			||||||
 | 
					        self.place.calculate_place_level()
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            5,
 | 
				
			||||||
 | 
					            self.place.level,
 | 
				
			||||||
 | 
					            msg='Expecting the default place level to be 5'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_level_mid_accuracy(self):
 | 
				
			||||||
 | 
					        explorer = self.place.submitted_by
 | 
				
			||||||
 | 
					        six_month_ago = datetime.timedelta(days=180)
 | 
				
			||||||
 | 
					        self.place.submitted_when = timezone.now() - six_month_ago
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        votings = [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=170),
 | 
				
			||||||
 | 
					                'vote': 5
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=23),
 | 
				
			||||||
 | 
					                'vote': 2
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=1),
 | 
				
			||||||
 | 
					                'vote': 4
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for vote in votings:
 | 
				
			||||||
 | 
					            voting = PlaceVoting.objects.create(
 | 
				
			||||||
 | 
					                submitted_by=explorer,
 | 
				
			||||||
 | 
					                place=self.place,
 | 
				
			||||||
 | 
					                vote= vote['vote']
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            voting.submitted_when = vote['date']
 | 
				
			||||||
 | 
					            voting.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            65,
 | 
				
			||||||
 | 
					            self.place.calculate_voting_accuracy()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_level_high_accuracy(self):
 | 
				
			||||||
 | 
					        explorer = self.place.submitted_by
 | 
				
			||||||
 | 
					        six_month_ago = datetime.timedelta(days=180)
 | 
				
			||||||
 | 
					        self.place.submitted_when = timezone.now() - six_month_ago
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        votings = [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=9),
 | 
				
			||||||
 | 
					                'vote': 5
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=14),
 | 
				
			||||||
 | 
					                'vote': 2
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=0),
 | 
				
			||||||
 | 
					                'vote': 4
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for vote in votings:
 | 
				
			||||||
 | 
					            voting = PlaceVoting.objects.create(
 | 
				
			||||||
 | 
					                submitted_by=explorer,
 | 
				
			||||||
 | 
					                place=self.place,
 | 
				
			||||||
 | 
					                vote= vote['vote']
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            voting.submitted_when = vote['date']
 | 
				
			||||||
 | 
					            voting.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            96,
 | 
				
			||||||
 | 
					            self.place.calculate_voting_accuracy()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_level_low_accuracy(self):
 | 
				
			||||||
 | 
					        explorer = self.place.submitted_by
 | 
				
			||||||
 | 
					        six_month_ago = datetime.timedelta(days=180)
 | 
				
			||||||
 | 
					        self.place.submitted_when = timezone.now() - six_month_ago
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        votings = [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=177),
 | 
				
			||||||
 | 
					                'vote': 5
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=150),
 | 
				
			||||||
 | 
					                'vote': 2
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=100),
 | 
				
			||||||
 | 
					                'vote': 4
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for vote in votings:
 | 
				
			||||||
 | 
					            voting = PlaceVoting.objects.create(
 | 
				
			||||||
 | 
					                submitted_by=explorer,
 | 
				
			||||||
 | 
					                place=self.place,
 | 
				
			||||||
 | 
					                vote= vote['vote']
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            voting.submitted_when = vote['date']
 | 
				
			||||||
 | 
					            voting.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            21,
 | 
				
			||||||
 | 
					            self.place.calculate_voting_accuracy()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_level_accuracy_zero_timedelta(self):
 | 
				
			||||||
 | 
					        explorer = self.place.submitted_by
 | 
				
			||||||
 | 
					        six_month_ago = datetime.timedelta(days=180)
 | 
				
			||||||
 | 
					        self.place.submitted_when = timezone.now() - six_month_ago
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        votings = [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'date': timezone.now() - datetime.timedelta(days=0),
 | 
				
			||||||
 | 
					                'vote': 4
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for vote in votings:
 | 
				
			||||||
 | 
					            voting = PlaceVoting.objects.create(
 | 
				
			||||||
 | 
					                submitted_by=explorer,
 | 
				
			||||||
 | 
					                place=self.place,
 | 
				
			||||||
 | 
					                vote= vote['vote']
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            voting.submitted_when = vote['date']
 | 
				
			||||||
 | 
					            voting.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            100,
 | 
				
			||||||
 | 
					            self.place.calculate_voting_accuracy(),
 | 
				
			||||||
 | 
					            msg='Expecting the accurcy to be 100% when the vote is 0 time units old'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								django_lostplaces/lostplaces/tests/views/im_a_image.jpeg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								django_lostplaces/lostplaces/tests/views/im_a_image.jpeg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 81 KiB  | 
@@ -1,4 +1,4 @@
 | 
				
			|||||||
23238#!/usr/bin/env python
 | 
					#!/usr/bin/env python
 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.test import TestCase, RequestFactory, Client
 | 
					from django.test import TestCase, RequestFactory, Client
 | 
				
			||||||
@@ -7,6 +7,7 @@ from django.db import models
 | 
				
			|||||||
from django.contrib.auth.models import User, AnonymousUser
 | 
					from django.contrib.auth.models import User, AnonymousUser
 | 
				
			||||||
from django.contrib.messages.storage.fallback import FallbackStorage
 | 
					from django.contrib.messages.storage.fallback import FallbackStorage
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from django.shortcuts import render
 | 
					from django.shortcuts import render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces.models import Place
 | 
					from lostplaces.models import Place
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										300
									
								
								django_lostplaces/lostplaces/tests/views/test_explorer_views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								django_lostplaces/lostplaces/tests/views/test_explorer_views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,300 @@
 | 
				
			|||||||
 | 
					import os
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.tests.views import (
 | 
				
			||||||
 | 
					    ViewTestCase,
 | 
				
			||||||
 | 
					    GlobalTemplateTestCaseMixin
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from lostplaces.views import ExplorerProfileView
 | 
				
			||||||
 | 
					from lostplaces.models import(
 | 
				
			||||||
 | 
					    Place,
 | 
				
			||||||
 | 
					    PlaceImage,
 | 
				
			||||||
 | 
					    PhotoAlbum
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestExplorerProfileView(GlobalTemplateTestCaseMixin, ViewTestCase):
 | 
				
			||||||
 | 
					    view = ExplorerProfileView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        user = User.objects.create_user(
 | 
				
			||||||
 | 
					            username='testpeter',
 | 
				
			||||||
 | 
					            password='Develop123'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_unauth_profile_access(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_profile', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpCode(response, 302)
 | 
				
			||||||
 | 
					        self.assertFalse(
 | 
				
			||||||
 | 
					            user.username in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting the username to not be visible to unauthorized users'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_unauth_profile_access_follow_redirect(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_profile', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					            follow=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            _('Please login to proceed') in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting a message to tell the user to login'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_explorer_places(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        Place.objects.create(
 | 
				
			||||||
 | 
					            name='Im the latest place 4369',
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            location='Test %d town' % 5,
 | 
				
			||||||
 | 
					            latitude=50.5 + 5/10,
 | 
				
			||||||
 | 
					            longitude=7.0 - 5/10,
 | 
				
			||||||
 | 
					            description='This is just a test, do not worry %d' % 5,
 | 
				
			||||||
 | 
					            level=3
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_profile', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'Im the latest place 4369' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting the latest place to be visible on the submitters profile page'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_draft_in_explorer_places(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        Place.objects.create(
 | 
				
			||||||
 | 
					            name='Im the latest place in draft 3671',
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            location='Test %d town' % 5,
 | 
				
			||||||
 | 
					            latitude=50.5 + 5/10,
 | 
				
			||||||
 | 
					            longitude=7.0 - 5/10,
 | 
				
			||||||
 | 
					            description='This is just a test, do not worry %d' % 5,
 | 
				
			||||||
 | 
					            level=3,
 | 
				
			||||||
 | 
					            mode='draft'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_profile', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertFalse(
 | 
				
			||||||
 | 
					            'Im the latest place in draft 3671' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting a place in draft mode to not show up on the submitters profile page'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_explorer_image(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        place = Place.objects.create(
 | 
				
			||||||
 | 
					            name='Im a the latest place 4369',
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            location='Test %d town' % 5,
 | 
				
			||||||
 | 
					            latitude=50.5 + 5/10,
 | 
				
			||||||
 | 
					            longitude=7.0 - 5/10,
 | 
				
			||||||
 | 
					            description='This is just a test, do not worry %d' % 5,
 | 
				
			||||||
 | 
					            level=3
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        current_dir = os.path.dirname(os.path.abspath(__file__))
 | 
				
			||||||
 | 
					        file_path = os.path.join(
 | 
				
			||||||
 | 
					            settings.MEDIA_ROOT, 
 | 
				
			||||||
 | 
					            settings.RELATIVE_THUMBNAIL_PATH,
 | 
				
			||||||
 | 
					            'im_a_image_3649.jpeg'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        shutil.copyfile(
 | 
				
			||||||
 | 
					            os.path.join(current_dir, 'im_a_image.jpeg'),
 | 
				
			||||||
 | 
					            file_path
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        PlaceImage.objects.create(
 | 
				
			||||||
 | 
					            description='Im a description',
 | 
				
			||||||
 | 
					            filename=file_path,
 | 
				
			||||||
 | 
					            place=place,
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_profile', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            os.path.join(settings.RELATIVE_THUMBNAIL_PATH,'im_a_image_3649.jpeg') in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting the latest place image to be visible on the submitters profile page'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_explorer_photoalbum(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        place = Place.objects.create(
 | 
				
			||||||
 | 
					            name='Im a the latest place 4369',
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            location='Test %d town' % 5,
 | 
				
			||||||
 | 
					            latitude=50.5 + 5/10,
 | 
				
			||||||
 | 
					            longitude=7.0 - 5/10,
 | 
				
			||||||
 | 
					            description='This is just a test, do not worry %d' % 5,
 | 
				
			||||||
 | 
					            level=3
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PhotoAlbum.objects.create(
 | 
				
			||||||
 | 
					            place=place,
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            url='http://example.org/6897134',
 | 
				
			||||||
 | 
					            label='Im a exmpale link label 6423'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_profile', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'href="http://example.org/6897134"' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting the latest photoalbum url to be linked on the submitters profile page'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'Im a exmpale link label 6423' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting the latest photoalbum label to be on the submitters profile page'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					class TestExplorerDraftsView(GlobalTemplateTestCaseMixin, ViewTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        user = User.objects.create_user(
 | 
				
			||||||
 | 
					            username='testpeter',
 | 
				
			||||||
 | 
					            password='Develop123'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        User.objects.create_user(
 | 
				
			||||||
 | 
					            username='otheruser',
 | 
				
			||||||
 | 
					            password='Develop123'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        superuser = User.objects.create_user(
 | 
				
			||||||
 | 
					            username='toor',
 | 
				
			||||||
 | 
					            password='Develop123'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        superuser.is_superuser = True
 | 
				
			||||||
 | 
					        superuser.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_draft_view(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_drafts', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_draft_view_unauthorized_user(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        self.client.login(username='otheruser', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_drafts', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertHttpForbidden(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_draft_view_superuser(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        self.client.login(username='toor', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_drafts', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def test_place_in_draft_view(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        Place.objects.create(
 | 
				
			||||||
 | 
					            name='Im a draft place 3792',
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            location='Test %d town' % 5,
 | 
				
			||||||
 | 
					            latitude=50.5 + 5/10,
 | 
				
			||||||
 | 
					            longitude=7.0 - 5/10,
 | 
				
			||||||
 | 
					            description='This is just a test, do not worry %d' % 5,
 | 
				
			||||||
 | 
					            mode='draft',
 | 
				
			||||||
 | 
					            level=3
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_drafts', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'Im a draft place 3792' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting a place draft to be visible in the submitters drafs view'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_place_not_in_draft_view(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        Place.objects.create(
 | 
				
			||||||
 | 
					            name='Im a draft place 3819',
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            location='Test %d town' % 5,
 | 
				
			||||||
 | 
					            latitude=50.5 + 5/10,
 | 
				
			||||||
 | 
					            longitude=7.0 - 5/10,
 | 
				
			||||||
 | 
					            description='This is just a test, do not worry %d' % 5,
 | 
				
			||||||
 | 
					            level=3
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('explorer_drafts', kwargs={
 | 
				
			||||||
 | 
					                'explorer_id': user.explorer.id
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertFalse(
 | 
				
			||||||
 | 
					            'Im a draft place 3819' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting a live place to not be visible in the submitters drafs view'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
@@ -13,6 +13,7 @@ from django.utils.translation import gettext as _
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces.models import Place
 | 
					from lostplaces.models import Place
 | 
				
			||||||
 | 
					from lostplaces.models import PLACE_MODES
 | 
				
			||||||
from lostplaces.views import (
 | 
					from lostplaces.views import (
 | 
				
			||||||
    PlaceCreateView,
 | 
					    PlaceCreateView,
 | 
				
			||||||
    PlaceListView,
 | 
					    PlaceListView,
 | 
				
			||||||
@@ -155,6 +156,38 @@ class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
 | 
				
			|||||||
            msg='Expecting the user to see places where their level is high enough'
 | 
					            msg='Expecting the user to see places where their level is high enough'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_place_mode_filter(self):
 | 
				
			||||||
 | 
					        explorer = User.objects.get(username='testpeter').explorer
 | 
				
			||||||
 | 
					        Place.objects.all().delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for mode in PLACE_MODES:
 | 
				
			||||||
 | 
					            place = Place.objects.create(
 | 
				
			||||||
 | 
					                name='Im a place in mode %s' % mode[0],
 | 
				
			||||||
 | 
					                submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					                submitted_by=explorer,
 | 
				
			||||||
 | 
					                location='Test town',
 | 
				
			||||||
 | 
					                latitude=50.5,
 | 
				
			||||||
 | 
					                longitude=7.0,
 | 
				
			||||||
 | 
					                description='This is just a test, do not worry %s' % mode[0],
 | 
				
			||||||
 | 
					                level=3,
 | 
				
			||||||
 | 
					                mode=mode[0]
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(reverse('place_list'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for mode in PLACE_MODES:
 | 
				
			||||||
 | 
					            if ('Im a place in mode %s' % mode[0]) in response.content.decode():
 | 
				
			||||||
 | 
					                self.assertTrue(
 | 
				
			||||||
 | 
					                    mode[0] == 'live',
 | 
				
			||||||
 | 
					                    msg='Expecting only places in mode \'live\' to be listed, saw a place in mode %s' % mode[0]
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            elif mode[0] == 'live':
 | 
				
			||||||
 | 
					                self.fail(
 | 
				
			||||||
 | 
					                    msg='Expecting at least one place in mode \'live\' to be listed'
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
class TestPlaceCreateView(ViewTestCase):
 | 
					class TestPlaceCreateView(ViewTestCase):
 | 
				
			||||||
    view = PlaceCreateView
 | 
					    view = PlaceCreateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -209,6 +242,11 @@ class TestPlaceCreateView(ViewTestCase):
 | 
				
			|||||||
            'success',
 | 
					            'success',
 | 
				
			||||||
            msg='Expecting a visible success message'
 | 
					            msg='Expecting a visible success message'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            Place.objects.get(name='test place 486').mode,
 | 
				
			||||||
 | 
					            'live',
 | 
				
			||||||
 | 
					            msg='Expeting the place to be in \'live\' mode'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    def test_positive_image(self):
 | 
					    def test_positive_image(self):
 | 
				
			||||||
        self.client.login(username='testpeter', password='Develop123')
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
@@ -258,6 +296,11 @@ class TestPlaceCreateView(ViewTestCase):
 | 
				
			|||||||
            'success',
 | 
					            'success',
 | 
				
			||||||
            msg='Expecting a visible success message'
 | 
					            msg='Expecting a visible success message'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            Place.objects.get(name='test place 894').mode,
 | 
				
			||||||
 | 
					            'live',
 | 
				
			||||||
 | 
					            msg='Expeting the place to be in \'live\' mode'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    def test_negative_no_name(self):
 | 
					    def test_negative_no_name(self):
 | 
				
			||||||
        self.client.login(username='testpeter', password='Develop123')
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
@@ -363,6 +406,31 @@ class TestPlaceCreateView(ViewTestCase):
 | 
				
			|||||||
            msg='Expecing a visible error message'
 | 
					            msg='Expecing a visible error message'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_positve_save_as_draft(self):
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            reverse('place_create'),
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                'name': 'test name 6483',
 | 
				
			||||||
 | 
					                'location': 'wurstwasser',
 | 
				
			||||||
 | 
					                'latitude': 45.4654,
 | 
				
			||||||
 | 
					                'longitude': 68.135489,
 | 
				
			||||||
 | 
					                'description': """
 | 
				
			||||||
 | 
					                Cupiditate harum reprehenderit ipsam iure consequuntur eaque eos reiciendis. Blanditiis vel minima minus repudiandae voluptate aut quia sed. Provident ex omnis illo molestiae. Ullam eos et est provident enim deserunt.
 | 
				
			||||||
 | 
					                """,
 | 
				
			||||||
 | 
					                'draft': True
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            follow=True
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            Place.objects.get(name='test name 6483').mode,
 | 
				
			||||||
 | 
					            'draft',
 | 
				
			||||||
 | 
					            msg='Expeting the place to be in \'draft\' mode'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixin, ViewTestCase):
 | 
					class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixin, ViewTestCase):
 | 
				
			||||||
    view = PlaceDetailView
 | 
					    view = PlaceDetailView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -416,6 +484,7 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
 | 
				
			|||||||
        self.client.login(username='blubberbernd', password='Develop123')
 | 
					        self.client.login(username='blubberbernd', password='Develop123')
 | 
				
			||||||
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
 | 
					        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpForbidden(response)
 | 
				
			||||||
        self.assertFalse(
 | 
					        self.assertFalse(
 | 
				
			||||||
            'Im a place' in response.content.decode(),
 | 
					            'Im a place' in response.content.decode(),
 | 
				
			||||||
            msg='Expecting the user to not see the places'
 | 
					            msg='Expecting the user to not see the places'
 | 
				
			||||||
@@ -425,6 +494,7 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
 | 
				
			|||||||
        self.client.login(username='toor', password='Develop123')
 | 
					        self.client.login(username='toor', password='Develop123')
 | 
				
			||||||
        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
 | 
					        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertTrue(
 | 
				
			||||||
            'Im a place' in response.content.decode(),
 | 
					            'Im a place' in response.content.decode(),
 | 
				
			||||||
            msg='Expecting the superuser to see all places'
 | 
					            msg='Expecting the superuser to see all places'
 | 
				
			||||||
@@ -434,6 +504,7 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
 | 
				
			|||||||
        self.client.login(username='blubberbernd', password='Develop123')
 | 
					        self.client.login(username='blubberbernd', password='Develop123')
 | 
				
			||||||
        response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
 | 
					        response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertTrue(
 | 
				
			||||||
            'Im a own place' in response.content.decode(),
 | 
					            'Im a own place' in response.content.decode(),
 | 
				
			||||||
            msg='Expecting the user to see it\'s own places'
 | 
					            msg='Expecting the user to see it\'s own places'
 | 
				
			||||||
@@ -443,6 +514,7 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
 | 
				
			|||||||
        self.client.login(username='testpeter', password='Develop123')
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
        response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
 | 
					        response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertTrue(
 | 
				
			||||||
            'Im a own place' in response.content.decode(),
 | 
					            'Im a own place' in response.content.decode(),
 | 
				
			||||||
            msg='Expecting the user to see places where their level is high enough'
 | 
					            msg='Expecting the user to see places where their level is high enough'
 | 
				
			||||||
@@ -606,5 +678,62 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
 | 
				
			|||||||
            msg='Expecting the explorer to not be in the reverse list of visits after deleting visit'
 | 
					            msg='Expecting the explorer to not be in the reverse list of visits after deleting visit'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_accessing_place_in_draft(self):
 | 
				
			||||||
 | 
					        user = User.objects.get(username='testpeter')
 | 
				
			||||||
 | 
					        place = Place.objects.create(
 | 
				
			||||||
 | 
					            name='Im a own place in draft 387948',
 | 
				
			||||||
 | 
					            submitted_when=timezone.now(),
 | 
				
			||||||
 | 
					            submitted_by=user.explorer,
 | 
				
			||||||
 | 
					            location='Test %d town' % 5,
 | 
				
			||||||
 | 
					            latitude=50.5 + 5/10,
 | 
				
			||||||
 | 
					            longitude=7.0 - 5/10,
 | 
				
			||||||
 | 
					            description='This is just a test, do not worry %d' % 5,
 | 
				
			||||||
 | 
					            level=3,
 | 
				
			||||||
 | 
					            mode='draft'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('place_detail', kwargs={
 | 
				
			||||||
 | 
					                'pk': place.pk
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'Im a own place in draft 387948' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting the user to see his own place in draft mode'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='blubberbernd', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('place_detail', kwargs={
 | 
				
			||||||
 | 
					                'pk': place.pk
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertHttpForbidden(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.client.login(username='toor', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(
 | 
				
			||||||
 | 
					            reverse('place_detail', kwargs={
 | 
				
			||||||
 | 
					                'pk': place.pk
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertHttpOK(response)
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'Im a own place in draft 387948' in response.content.decode(),
 | 
				
			||||||
 | 
					            msg='Expecting a superuser to see all places in draft mode'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@@ -23,7 +23,10 @@ from lostplaces.views import (
 | 
				
			|||||||
	PhotoAlbumDeleteView,
 | 
						PhotoAlbumDeleteView,
 | 
				
			||||||
    ExplorerProfileView,
 | 
					    ExplorerProfileView,
 | 
				
			||||||
    ExplorerProfileUpdateView,
 | 
					    ExplorerProfileUpdateView,
 | 
				
			||||||
    PlaceVoteView
 | 
					    ExplorerDraftsView,
 | 
				
			||||||
 | 
					    PlaceVoteView,
 | 
				
			||||||
 | 
					    UploadMapFileView,
 | 
				
			||||||
 | 
					    ImportDetailView
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
@@ -33,6 +36,7 @@ urlpatterns = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    path('explorer/<int:explorer_id>/', ExplorerProfileView.as_view(), name='explorer_profile'),
 | 
					    path('explorer/<int:explorer_id>/', ExplorerProfileView.as_view(), name='explorer_profile'),
 | 
				
			||||||
    path('explorer/update/', ExplorerProfileUpdateView.as_view(), name='explorer_profile_update'),
 | 
					    path('explorer/update/', ExplorerProfileUpdateView.as_view(), name='explorer_profile_update'),
 | 
				
			||||||
 | 
					    path('explorer/<int:explorer_id>/drafts', ExplorerDraftsView.as_view(), name='explorer_drafts'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    path('place/', PlaceListView.as_view(), name='place_list'),
 | 
					    path('place/', PlaceListView.as_view(), name='place_list'),
 | 
				
			||||||
    path('place/<int:pk>/', PlaceDetailView.as_view(), name='place_detail'),
 | 
					    path('place/<int:pk>/', PlaceDetailView.as_view(), name='place_detail'),
 | 
				
			||||||
@@ -52,5 +56,8 @@ urlpatterns = [
 | 
				
			|||||||
    path('place/vote/<int:place_id>/<int:vote>', PlaceVoteView.as_view(), name='place_vote'),
 | 
					    path('place/vote/<int:place_id>/<int:vote>', PlaceVoteView.as_view(), name='place_vote'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	path('photo_album/create/<int:place_id>/', PhotoAlbumCreateView.as_view(), name='photo_album_create'),
 | 
						path('photo_album/create/<int:place_id>/', PhotoAlbumCreateView.as_view(), name='photo_album_create'),
 | 
				
			||||||
	path('photo_album/delete/<int:pk>/', PhotoAlbumDeleteView.as_view(), name='photo_album_delete')
 | 
						path('photo_album/delete/<int:pk>/', PhotoAlbumDeleteView.as_view(), name='photo_album_delete'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    path('import/upload', UploadMapFileView.as_view(), name='import_upload'),
 | 
				
			||||||
 | 
					    path('import/<int:pk>', ImportDetailView.as_view(), name='import_detail')
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,3 +6,4 @@ from lostplaces.views.views import *
 | 
				
			|||||||
from lostplaces.views.place_views import *
 | 
					from lostplaces.views.place_views import *
 | 
				
			||||||
from lostplaces.views.place_image_views import *
 | 
					from lostplaces.views.place_image_views import *
 | 
				
			||||||
from lostplaces.views.explorer_views import *
 | 
					from lostplaces.views.explorer_views import *
 | 
				
			||||||
 | 
					from lostplaces.views.imports import *
 | 
				
			||||||
@@ -31,6 +31,21 @@ class IsAuthenticatedMixin(LoginRequiredMixin, View):
 | 
				
			|||||||
            messages.error(self.request, self.permission_denied_message)
 | 
					            messages.error(self.request, self.permission_denied_message)
 | 
				
			||||||
        return super().handle_no_permission()
 | 
					        return super().handle_no_permission()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IsSuperUserMixin(UserPassesTestMixin, View):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    A view mixin that checks if the user is a superuser.
 | 
				
			||||||
 | 
					    Users who are not logged in or users who are no superuser get
 | 
				
			||||||
 | 
					    a permission deined message.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    permission_denied_message = _('You are not allowed to see this page')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_func(self):
 | 
				
			||||||
 | 
					        return self.request.user.is_superuser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_no_permission(self):
 | 
				
			||||||
 | 
					        messages.error(self.request, self.permission_denied_message)
 | 
				
			||||||
 | 
					        return super().handle_no_permission()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IsPlaceSubmitterMixin(UserPassesTestMixin, View):
 | 
					class IsPlaceSubmitterMixin(UserPassesTestMixin, View):
 | 
				
			||||||
    '''
 | 
					    '''
 | 
				
			||||||
    A view mixin that checks wether a user is the submitter
 | 
					    A view mixin that checks wether a user is the submitter
 | 
				
			||||||
@@ -138,4 +153,10 @@ class LevelCapPlaceListView(ListView):
 | 
				
			|||||||
    model = Place
 | 
					    model = Place
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def get_queryset(self):
 | 
					    def get_queryset(self):
 | 
				
			||||||
        return self.request.user.explorer.get_places_eligible_to_see()
 | 
					        return self.request.user.explorer.get_place_list_to_display()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					        context['place_list'] = context.pop('object_list')
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
@@ -8,6 +8,7 @@ from django.utils.translation import gettext as _
 | 
				
			|||||||
from django.shortcuts import render, redirect, get_object_or_404
 | 
					from django.shortcuts import render, redirect, get_object_or_404
 | 
				
			||||||
from django.urls import reverse_lazy
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
 | 
					from django.contrib.auth.mixins import UserPassesTestMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces.common import get_all_subclasses
 | 
					from lostplaces.common import get_all_subclasses
 | 
				
			||||||
from lostplaces.views.base_views import IsAuthenticatedMixin
 | 
					from lostplaces.views.base_views import IsAuthenticatedMixin
 | 
				
			||||||
@@ -18,7 +19,7 @@ from lostplaces.forms import ExplorerChangeForm, ExplorerUserChangeForm
 | 
				
			|||||||
class ExplorerProfileView(IsAuthenticatedMixin, View):
 | 
					class ExplorerProfileView(IsAuthenticatedMixin, View):
 | 
				
			||||||
    def get(self, request, explorer_id):
 | 
					    def get(self, request, explorer_id):
 | 
				
			||||||
        explorer = get_object_or_404(Explorer, pk=explorer_id)
 | 
					        explorer = get_object_or_404(Explorer, pk=explorer_id)
 | 
				
			||||||
        place_list = explorer.places.all()
 | 
					        place_list = explorer.get_place_list_to_display()
 | 
				
			||||||
        place_count = place_list.count()
 | 
					        place_count = place_list.count()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        context={
 | 
					        context={
 | 
				
			||||||
@@ -82,3 +83,20 @@ class ExplorerProfileUpdateView(IsAuthenticatedMixin, View):
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
        return redirect(reverse_lazy('explorer_profile_update'))
 | 
					        return redirect(reverse_lazy('explorer_profile_update'))
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					class ExplorerDraftsView(IsAuthenticatedMixin, UserPassesTestMixin, View):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_explorer(self):
 | 
				
			||||||
 | 
					        return get_object_or_404(Explorer, pk=self.kwargs['explorer_id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_func(self):
 | 
				
			||||||
 | 
					        if not hasattr(self.request, 'user'):
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        return self.request.user == self.get_explorer().user or self.request.user.is_superuser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        context = {
 | 
				
			||||||
 | 
					            'explorer': self.get_explorer(),
 | 
				
			||||||
 | 
					            'place_list': self.get_explorer().get_drafts()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return render(request, 'explorer/drafts.html', context)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
							
								
								
									
										91
									
								
								django_lostplaces/lostplaces/views/imports.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								django_lostplaces/lostplaces/views/imports.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					from django.views import View
 | 
				
			||||||
 | 
					from django.shortcuts import render, redirect, get_object_or_404
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					from django.core.paginator import Paginator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from pykml import parser as kml_parser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.forms import UploadMapFileForm
 | 
				
			||||||
 | 
					from lostplaces.models import Place, PlaceImport
 | 
				
			||||||
 | 
					from lostplaces.views.base_views import (
 | 
				
			||||||
 | 
					    IsSuperUserMixin
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UploadMapFileView(IsSuperUserMixin, View):
 | 
				
			||||||
 | 
					    permission_denied_message = _('You are not allowed to import any places')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request):
 | 
				
			||||||
 | 
					        upload_form = UploadMapFileForm()
 | 
				
			||||||
 | 
					        return render(request, 'import/upload_map_file.html', {'upload_form': upload_form})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def post(self, request):
 | 
				
			||||||
 | 
					        upload_form = UploadMapFileForm(request.POST, request.FILES)
 | 
				
			||||||
 | 
					        explorer = request.user.explorer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if upload_form.is_valid():
 | 
				
			||||||
 | 
					            map_file = upload_form.cleaned_data['map_file']
 | 
				
			||||||
 | 
					            parsed_kml = kml_parser.fromstring(map_file.read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            place_import = PlaceImport.objects.create(
 | 
				
			||||||
 | 
					                explorer=request.user.explorer,
 | 
				
			||||||
 | 
					                description=upload_form.cleaned_data['description']
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for folder in parsed_kml.Document.Folder:
 | 
				
			||||||
 | 
					                for place_kml in folder.Placemark:
 | 
				
			||||||
 | 
					                    lat_long = self.get_lat_long(place_kml)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    name = str(place_kml.name) if hasattr(place_kml, 'name')  else ''
 | 
				
			||||||
 | 
					                    description = str(place_kml.description) if hasattr(place_kml, 'description') else ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    place_model = Place.objects.create(
 | 
				
			||||||
 | 
					                        name=name.strip(),
 | 
				
			||||||
 | 
					                        latitude=lat_long[0],
 | 
				
			||||||
 | 
					                        longitude=lat_long[1],
 | 
				
			||||||
 | 
					                        description=description.strip(),
 | 
				
			||||||
 | 
					                        place_import=place_import,
 | 
				
			||||||
 | 
					                        mode='imported'
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            place_import.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return redirect(reverse('lostplaces_home'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_lat_long(self, place_kml):
 | 
				
			||||||
 | 
					        if hasattr(place_kml, 'Point') and len(place_kml.Point.coordinates) >= 1:
 | 
				
			||||||
 | 
					            coordinates = str(place_kml.Point.coordinates[0]).strip()
 | 
				
			||||||
 | 
					            splited = coordinates.split(',')
 | 
				
			||||||
 | 
					            latitude = 0
 | 
				
			||||||
 | 
					            longitude = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if len(splited) >= 1:
 | 
				
			||||||
 | 
					                longitude = splited[0]
 | 
				
			||||||
 | 
					            if len(splited) >= 2:
 | 
				
			||||||
 | 
					                latitude = splited[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return (latitude, longitude)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return (0, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ImportDetailView(IsSuperUserMixin, View):
 | 
				
			||||||
 | 
					    permission_denied_message = _('You are not allowed to see this import\'s details')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_import(self):
 | 
				
			||||||
 | 
					        return get_object_or_404(PlaceImport, pk=self.kwargs['pk'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        place_import = self.get_import()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        place_paginator = Paginator(place_import.place_list.all(), 18)
 | 
				
			||||||
 | 
					        paginated_places = place_paginator.get_page(
 | 
				
			||||||
 | 
					            request.GET.get('page')
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        context = {
 | 
				
			||||||
 | 
					            'import': place_import,
 | 
				
			||||||
 | 
					            'paginated_places': paginated_places,
 | 
				
			||||||
 | 
					            'import_type': place_import.get_import_type_display()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return render(request, 'import/import_detail_view.html', context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,7 +36,7 @@ from lostplaces.common import redirect_referer_or
 | 
				
			|||||||
from taggit.models import Tag
 | 
					from taggit.models import Tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlaceListView(IsAuthenticatedMixin, LevelCapPlaceListView):
 | 
					class PlaceListView(IsAuthenticatedMixin, LevelCapPlaceListView):
 | 
				
			||||||
    paginate_by = 5
 | 
					    paginate_by = 18
 | 
				
			||||||
    template_name = 'place/place_list.html'
 | 
					    template_name = 'place/place_list.html'
 | 
				
			||||||
    ordering = [Lower('name')]
 | 
					    ordering = [Lower('name')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,6 +59,17 @@ class PlaceDetailView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
 | 
				
			|||||||
        place.calculate_place_level()
 | 
					        place.calculate_place_level()
 | 
				
			||||||
        explorer = request.user.explorer
 | 
					        explorer = request.user.explorer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if place.mode == 'draft':
 | 
				
			||||||
 | 
					            messages.info(
 | 
				
			||||||
 | 
					                self.request,
 | 
				
			||||||
 | 
					                _('This place is still in draft mode and only visible to the submitter and superusers')
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        elif place.mode == 'imported':
 | 
				
			||||||
 | 
					            messages.info(
 | 
				
			||||||
 | 
					                self.request,
 | 
				
			||||||
 | 
					                _('This place was imported and not reviewed & correted yet. This place is visbible for superusers only and does not appear in the list views.')
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        context = {
 | 
					        context = {
 | 
				
			||||||
            'place': place,
 | 
					            'place': place,
 | 
				
			||||||
            'mapping_config': {
 | 
					            'mapping_config': {
 | 
				
			||||||
@@ -74,7 +85,8 @@ class PlaceDetailView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            'placevoting': {
 | 
					            'placevoting': {
 | 
				
			||||||
                'users_vote': PlaceVoting.objects.filter(place=place, submitted_by=explorer).first(),
 | 
					                'users_vote': PlaceVoting.objects.filter(place=place, submitted_by=explorer).first(),
 | 
				
			||||||
                'all_choices': reversed(PLACE_LEVELS)
 | 
					                'all_choices': reversed(PLACE_LEVELS),
 | 
				
			||||||
 | 
					                'accuracy': place.calculate_voting_accuracy()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return render(request, 'place/place_detail.html', context)
 | 
					        return render(request, 'place/place_detail.html', context)
 | 
				
			||||||
@@ -112,6 +124,9 @@ class PlaceCreateView(MultiplePlaceImageUploadMixin, IsAuthenticatedMixin, View)
 | 
				
			|||||||
            place = place_form.save(commit=False)
 | 
					            place = place_form.save(commit=False)
 | 
				
			||||||
            # Save logged in user as "submitted_by"
 | 
					            # Save logged in user as "submitted_by"
 | 
				
			||||||
            place.submitted_by = submitter
 | 
					            place.submitted_by = submitter
 | 
				
			||||||
 | 
					            if place_form.cleaned_data['draft']:
 | 
				
			||||||
 | 
					                place.mode = 'draft';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            place.save()
 | 
					            place.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.handle_place_images(request, place)
 | 
					            self.handle_place_images(request, place)
 | 
				
			||||||
@@ -204,8 +219,11 @@ class PlaceVisitDeleteView(IsAuthenticatedMixin, View):
 | 
				
			|||||||
class PlaceVoteView(IsEligibleToSeePlaceMixin, View):
 | 
					class PlaceVoteView(IsEligibleToSeePlaceMixin, View):
 | 
				
			||||||
    delta = timedelta(weeks=24)
 | 
					    delta = timedelta(weeks=24)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    def get_place(self):
 | 
				
			||||||
 | 
					        return get_object_or_404(Place, pk=self.kwargs['place_id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self, request, place_id, vote):
 | 
					    def get(self, request, place_id, vote):
 | 
				
			||||||
        place = get_object_or_404(Place, id=place_id)
 | 
					        place = self.get_place()
 | 
				
			||||||
        explorer = request.user.explorer
 | 
					        explorer = request.user.explorer
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        voting = PlaceVoting.objects.filter(
 | 
					        voting = PlaceVoting.objects.filter(
 | 
				
			||||||
@@ -217,12 +235,11 @@ class PlaceVoteView(IsEligibleToSeePlaceMixin, View):
 | 
				
			|||||||
            voting = PlaceVoting.objects.create(
 | 
					            voting = PlaceVoting.objects.create(
 | 
				
			||||||
                submitted_by=explorer,
 | 
					                submitted_by=explorer,
 | 
				
			||||||
                place=place,
 | 
					                place=place,
 | 
				
			||||||
                vote=vote,
 | 
					                vote=vote
 | 
				
			||||||
                expires_when=timezone.now()+self.delta
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            messages.success(self.request, _('Vote submitted'))
 | 
					            messages.success(self.request, _('Vote submitted'))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            voting.expires_when=timezone.now()+self.delta
 | 
					            voting.submitted_when = timezone.now()
 | 
				
			||||||
            voting.vote = vote
 | 
					            voting.vote = vote
 | 
				
			||||||
            messages.success(self.request, _('Your vote has been update'))
 | 
					            messages.success(self.request, _('Your vote has been update'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ from django.utils.translation import gettext as _
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from lostplaces.forms import SignupVoucherForm, TagSubmitForm
 | 
					from lostplaces.forms import SignupVoucherForm, TagSubmitForm
 | 
				
			||||||
from lostplaces.models import Place, PhotoAlbum
 | 
					from lostplaces.models import Place, PhotoAlbum
 | 
				
			||||||
from lostplaces.views.base_views import IsAuthenticatedMixin
 | 
					from lostplaces.views.base_views import IsAuthenticatedMixin, LevelCapPlaceListView
 | 
				
			||||||
from lostplaces.common import redirect_referer_or
 | 
					from lostplaces.common import redirect_referer_or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces.views.base_views import (
 | 
					from lostplaces.views.base_views import (
 | 
				
			||||||
@@ -29,17 +29,19 @@ class SignUpView(SuccessMessageMixin, CreateView):
 | 
				
			|||||||
    template_name = 'signup.html'
 | 
					    template_name = 'signup.html'
 | 
				
			||||||
    success_message = _('User created')
 | 
					    success_message = _('User created')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HomeView(IsAuthenticatedMixin, View):
 | 
					class HomeView(IsAuthenticatedMixin, LevelCapPlaceListView, View):
 | 
				
			||||||
    def get(self, request, *args, **kwargs):
 | 
					    template_name = 'home.html'
 | 
				
			||||||
        place_list = request.user.explorer.get_places_eligible_to_see()
 | 
					
 | 
				
			||||||
        context = {
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
            'place_list': place_list,
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
            'mapping_config': {
 | 
					        place_list = context['place_list']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context['mapping_config'] = {
 | 
				
			||||||
            'all_points': place_list,
 | 
					            'all_points': place_list,
 | 
				
			||||||
            'map_center': Place.average_latlon(place_list)
 | 
					            'map_center': Place.average_latlon(place_list)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        }
 | 
					        return context
 | 
				
			||||||
        return render(request, 'home.html', context)
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle_no_permission(self):
 | 
					    def handle_no_permission(self):
 | 
				
			||||||
        place_list = Place.objects.filter(level=1)[:5]
 | 
					        place_list = Place.objects.filter(level=1)[:5]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user