38 Commits

Author SHA1 Message Date
05481fc0c8 Refactoring _has_context_key 2020-09-13 12:41:31 +02:00
6b00452830 Refactoring 2020-09-13 12:39:46 +02:00
7e4c5dcf24 Adding more methods to viewtestcase 2020-09-13 12:39:02 +02:00
c0f30e56f7 Small tweak 2020-09-13 11:02:53 +02:00
9852646fff Renaming IsPlaceSubmitter 2020-09-13 10:57:53 +02:00
c2d678847e Renaming IsAuthenticated 2020-09-13 10:56:18 +02:00
e77edf18ac Testing Abstract classes 2020-09-13 10:27:01 +02:00
3780aa6cf1 Abtract classes 2020-09-12 12:24:27 +02:00
21124ec2ad adapting list views 2020-09-12 12:02:25 +02:00
0ee5fc59d3 Adapting tests 2020-09-12 12:02:17 +02:00
b8dfef691e Fixing bug; Place did not show up on map 2020-09-12 11:58:45 +02:00
a1886b0b60 Adapting home view 2020-09-12 11:47:42 +02:00
e1002b5315 Adapting place detail view 2020-09-12 11:42:31 +02:00
fed90d4f7b Refactoring osm map 2020-09-12 11:42:18 +02:00
dcfb329c5a Adapted template 2020-09-12 11:35:30 +02:00
b8a21a8baa Changed average_latlon 2020-09-12 11:34:49 +02:00
7f73035b02 Refactoring tagging 2020-09-12 11:24:16 +02:00
317437fedc Testing PlaceListView 2020-09-12 11:02:39 +02:00
26286984c2 Base functions for testing views 2020-09-12 11:02:23 +02:00
9ae31c0146 Removing obsolote code 2020-09-12 11:02:01 +02:00
87fd8fa96f Small fix 2020-09-12 11:01:34 +02:00
c3401e732f Removed obosolete code 2020-09-12 11:01:24 +02:00
4ee7373b3f Testing Placeimage file change 2020-09-12 08:48:53 +02:00
64c0c5f8e6 Setting Up Test Data mor in a Unit way 2020-09-12 08:39:06 +02:00
18a597c726 Merge branch 'develop' into testing 2020-09-12 08:38:37 +02:00
Leonhard Strohmidel
baca596603 sync 2020-09-11 23:07:19 +02:00
d993387216 Fixed text repr of Explorer model due to user model change. 2020-09-11 22:43:03 +02:00
aed2856df3 Made the test timezone aware, DateTimeFiled(auto_now_add) already was. 2020-09-11 22:22:03 +02:00
c78858c152 Changed class attibutes to match test expectation. 2020-09-11 22:03:20 +02:00
f49581259e Typo in class name. 2020-09-11 19:32:07 +02:00
f5bf642cd6 Ups, not for the settings file. 2020-09-11 19:23:13 +02:00
7687acb366 Changed scope (?) of remaining local imports. 2020-09-11 19:15:39 +02:00
e655e1598a Trying to test deletion, wip 2020-09-11 12:09:51 +02:00
64ed38332f Refactoring and testing __str__ 2020-09-11 12:09:34 +02:00
d438303aec file for model tests 2020-09-11 12:08:43 +02:00
38b3736951 New Model tests 2020-09-11 12:08:27 +02:00
6be060ea40 Refactoring and more keywords to test 2020-09-11 12:08:15 +02:00
5c5756150f More dev dep 2020-09-11 12:07:57 +02:00
24 changed files with 693 additions and 307 deletions

View File

@@ -11,6 +11,7 @@ pipenv = "*"
wheel = "*" wheel = "*"
twine = "*" twine = "*"
pandoc ="*" pandoc ="*"
pylint-django ="*"
[packages] [packages]
django = "*" django = "*"
@@ -18,6 +19,7 @@ easy-thumbnails = "*"
image = "*" image = "*"
django-widget-tweaks = "*" django-widget-tweaks = "*"
django-taggit = "*" django-taggit = "*"
# Commented out to not explicitly specify Python 3 subversion. # Commented out to not explicitly specify Python 3 subversion.
# [requires] [requires]
# python_version = "3.8" python_version = "3.7"

View File

@@ -6,15 +6,15 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from .models import * from lostplaces_app.models import *
from .forms import ExplorerCreationForm, ExplorerChangeForm from lostplaces_app.forms import ExplorerCreationForm, ExplorerChangeForm
# Register your models here. # Register your models here.
class VoucherAdmin(admin.ModelAdmin): class VoucherAdmin(admin.ModelAdmin):
fields = ['code', 'expires', 'created'] fields = ['code', 'expires_when', 'created_when']
readonly_fields = ['created'] readonly_fields = ['created_when']
admin.site.register(Explorer) admin.site.register(Explorer)
admin.site.register(Voucher, VoucherAdmin) admin.site.register(Voucher, VoucherAdmin)

View File

@@ -9,6 +9,7 @@ database.
import os import os
import uuid import uuid
from django.urls import reverse
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.db.models.signals import post_save from django.db.models.signals import post_save
@@ -19,6 +20,31 @@ from taggit.managers import TaggableManager
# Create your models here. # Create your models here.
class Taggable(models.Model):
class Meta:
abstract = True
tags = TaggableManager(blank=True)
class MapablePoint(models.Model):
class Meta:
abstract = True
name = models.CharField(max_length=50)
latitude = models.FloatField(
validators=[
MinValueValidator(-90),
MaxValueValidator(90)
]
)
longitude = models.FloatField(
validators=[
MinValueValidator(-180),
MaxValueValidator(180)
]
)
class Explorer(models.Model): class Explorer(models.Model):
""" """
@@ -33,7 +59,7 @@ class Explorer(models.Model):
) )
def __str__(self): def __str__(self):
return self.user.name return self.user.username
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs): def create_user_profile(sender, instance, created, **kwargs):
@@ -54,19 +80,18 @@ class Voucher(models.Model):
""" """
code = models.CharField(unique=True, max_length=30) code = models.CharField(unique=True, max_length=30)
created = models.DateTimeField(auto_now_add=True) created_when = models.DateTimeField(auto_now_add=True)
expires = models.DateField() expires_when = models.DateTimeField()
def __str__(self): def __str__(self):
return "Voucher " + str(self.pk) return "Voucher " + str(self.code)
class Place (models.Model): class Place(Taggable, MapablePoint):
""" """
Place defines a lost place (location, name, description etc.). Place defines a lost place (location, name, description etc.).
""" """
name = models.CharField(max_length=50)
submitted_when = models.DateTimeField(auto_now_add=True, null=True) submitted_when = models.DateTimeField(auto_now_add=True, null=True)
submitted_by = models.ForeignKey( submitted_by = models.ForeignKey(
Explorer, Explorer,
@@ -76,24 +101,15 @@ class Place (models.Model):
related_name='places' related_name='places'
) )
location = models.CharField(max_length=50) location = models.CharField(max_length=50)
latitude = models.FloatField(
validators=[
MinValueValidator(-90),
MaxValueValidator(90)
]
)
longitude = models.FloatField(
validators=[
MinValueValidator(-180),
MaxValueValidator(180)
]
)
description = models.TextField() description = models.TextField()
tags = TaggableManager(blank=True) def get_absolute_url(self):
return reverse('place_detail', kwargs={'pk': self.pk})
@classmethod
# Get center position of LP-geocoordinates. # Get center position of LP-geocoordinates.
def average_latlon(place_list): def average_latlon(cls, place_list):
amount = len(place_list) amount = len(place_list)
# Init fill values to prevent None # Init fill values to prevent None
longitude = 0 longitude = 0
@@ -103,9 +119,9 @@ class Place (models.Model):
for place in place_list: for place in place_list:
longitude += place.longitude longitude += place.longitude
latitude += place.latitude latitude += place.latitude
return (latitude / amount, longitude / amount) return {'latitude':latitude / amount, 'longitude': longitude / amount}
return (latitude, longitude) return {'latitude': latitude, 'longitude': longitude}
def __str__(self): def __str__(self):
return self.name return self.name

View File

@@ -2,8 +2,8 @@
{% load static %} {% load static %}
{% block additional_head %} {% block additional_head %}
<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
<script src="{% static 'maps/ol.js' %}"></script> <script src="{% static 'maps/ol.js' %}"></script>
{% endblock additional_head %} {% endblock additional_head %}
# {% block title %}Start{% endblock %} # {% block title %}Start{% endblock %}
@@ -14,7 +14,7 @@
<article class="LP-TextSection"> <article class="LP-TextSection">
</article> </article>
{% include 'partials/osm_map.html' %} {% include 'partials/osm_map.html' with config=map_config %}
<div class="LP-PlaceGrid"> <div class="LP-PlaceGrid">
<h1 class="LP-Headline LP-Headline">Explore the latest locations</h1> <h1 class="LP-Headline LP-Headline">Explore the latest locations</h1>
<ul class="LP-PlaceGrid__Grid"> <ul class="LP-PlaceGrid__Grid">

View File

@@ -11,20 +11,20 @@
}), }),
], ],
view: new ol.View({ view: new ol.View({
center: ol.proj.fromLonLat([{{place_map_center|last}}, {{place_map_center|first}}]), center: ol.proj.fromLonLat([{{config.map_center.longitude}}, {{config.map_center.latitude}}]),
zoom: 9 zoom: 9
}) })
}); });
var vectorSource = new ol.source.Vector({ var vectorSource = new ol.source.Vector({
features: [ features: [
{% for place in place_list %} {% for point in config.point_list %}
new ol.Feature({ new ol.Feature({
geometry: new ol.geom.Point( geometry: new ol.geom.Point(
ol.proj.fromLonLat([{{place.longitude}},{{place.latitude}}]) ol.proj.fromLonLat([{{point.longitude}},{{point.latitude}}])
), ),
url: '{% url 'place_detail' pk=place.pk %}', url: '{{point.get_absolute_url}}',
name: '{{place.name}}' name: ' {{point.name}}'
}), }),
{% endfor %} {% endfor %}
] ]

View File

@@ -1,6 +1,6 @@
<div class="LP-TagList"> <div class="LP-TagList">
<ul class="LP-TagList__List"> <ul class="LP-TagList__List">
{% for tag in tag_list %} {% for tag in config.tagged_item.tags.all %}
<li class="LP-TagList__Item"> <li class="LP-TagList__Item">
<div class="LP-Tag"> <div class="LP-Tag">
<a href="#" class="LP-Link"> <a href="#" class="LP-Link">
@@ -23,7 +23,7 @@
</ul> </ul>
</div> </div>
<form id="id_tag_submit_form" class="LP-Form LP-Form--inline LP-Form--tagging" method="POST" action="{{config.submit_url}}"> <form id="id_tag_submit_form" class="LP-Form LP-Form--inline LP-Form--tagging" method="POST" action="{% url config.submit_url_name tagged_id=config.tagged_item.id%}">
<fieldset class="LP-Form__Fieldset"> <fieldset class="LP-Form__Fieldset">
<legend class="LP-Form__Legend">Tags hinzufügen</legend> <legend class="LP-Form__Legend">Tags hinzufügen</legend>
{% csrf_token %} {% csrf_token %}
@@ -46,14 +46,10 @@
submit_form.onsubmit = () => false submit_form.onsubmit = () => false
const tagify = new Tagify(input, { const tagify = new Tagify(input, {
'whitelist': [{ 'whitelist': [
% {% for tag in config.tagged_item.tags.all %}
for tag in all_tags %
}
'{{tag}}', '{{tag}}',
{ {% endfor %}
% endfor %
}
] ]
}) })

View File

@@ -37,13 +37,13 @@
<section class="LP-Section"> <section class="LP-Section">
{% url 'place_tag_submit' place_id=place.id as tag_submit_url%} {% url 'place_tag_submit' place_id=place.id as tag_submit_url%}
{% include 'partials/tagging.html' with tag_list=place.tags.all config=tagging_config all_tags=all_tags %} {% include 'partials/tagging.html' with config=tagging_config %}
</section> </section>
<section class="LP-Section"> <section class="LP-Section">
<h1 class="LP-Headline">Map-Links</h1> <h1 class="LP-Headline">Map-Links</h1>
{% include 'partials/osm_map.html' %} {% include 'partials/osm_map.html' with config=map_config%}
<div class="LP-LinkList"> <div class="LP-LinkList">
<ul class="LP-LinkList__Container"> <ul class="LP-LinkList__Container">
<li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude}},{{place.longitude}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li> <li class="LP-LinkList__Item"><a target="_blank" href="https://www.google.com/maps?q={{place.latitude}},{{place.longitude}}" class="LP-Link"><span class="LP-Text">Google Maps</span></a></li>

View File

@@ -2,54 +2,54 @@
{% load static %} {% load static %}
{% block additional_head %} {% block additional_head %}
<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
<script src="{% static 'maps/ol.js' %}"></script> <script src="{% static 'maps/ol.js' %}"></script>
{% endblock additional_head %} {% endblock additional_head %}
{% block title %}Lost Places{% endblock %} {% block title %}Lost Places{% endblock %}
{% block maincontent %} {% block maincontent %}
{% include 'partials/osm_map.html' %} {% include 'partials/osm_map.html' with config=map_config %}
<div class="LP-PlaceList"> <div class="LP-PlaceList">
<h1 class="LP-Headline">Listing our places</h1> <h1 class="LP-Headline">Listing our places</h1>
<ul class="LP-PlaceList__List"> <ul class="LP-PlaceList__List">
{% for place in place_list %} {% for place in place_list %}
<li class="LP-PlaceList__Item"> <li class="LP-PlaceList__Item">
<a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link"> <a href="{% url 'place_detail' pk=place.pk %}" class="LP-Link">
<article class="LP-PlaceTeaser LP-PlaceTeaser--extended"> <article class="LP-PlaceTeaser LP-PlaceTeaser--extended">
<div class="LP-PlaceTeaser__Image"> <div class="LP-PlaceTeaser__Image">
<img class="LP-Image" src="{{ place.images.first.filename.thumbnail.url }}" /> <img class="LP-Image" src="{{ place.images.first.filename.thumbnail.url }}" />
</div>
<div class="LP-PlaceTeaser__Meta">
<div class="LP-PlaceTeaser__Info">
<span class="LP-PlaceTeaser__Title">
<h2 class="LP-Headline LP-Headline--teaser">{{place.name}}</h2>
</span>
<span class="LP-PlaceTeaser__Detail">
<p class="LP-Paragraph">{{place.location}}</p>
</span>
</div> </div>
<div class="LP-PlaceTeaser__Meta"> <div class="LP-PlaceTeaser__Description">
<div class="LP-PlaceTeaser__Info"> <p class="LP-Paragraph">
<span class="LP-PlaceTeaser__Title"> {% if place.description|length > 210 %}
<h2 class="LP-Headline LP-Headline--teaser">{{place.name}}</h2> {{place.description|truncatechars:210|truncatewords:-1}}
</span> {% else %}
<span class="LP-PlaceTeaser__Detail"> {{place.description}}
<p class="LP-Paragraph">{{place.location}}</p> {% endif %}
</span> </p>
</div> </div>
<div class="LP-PlaceTeaser__Description"> <div class="LP-PlaceTeaser__Icons">
<p class="LP-Paragraph"> <ul class="LP-Icon__List">
{% if place.description|length > 210 %} <li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/favourite.svg' %}" /></li>
{{place.description|truncatechars:210|truncatewords:-1}}
{% else %}
{{place.description}}
{% endif %}
</p>
</div>
<div class="LP-PlaceTeaser__Icons">
<ul class="LP-Icon__List">
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/favourite.svg' %}" /></li>
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/location.svg' %}" /></li> <li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/location.svg' %}" /></li>
<li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li> <li class="LP-Icon__Item"><img class="LP-Icon" src="{% static '/icons/flag.svg' %}" /></li>
</ul> </ul>
</div>
</div> </div>
</article> </div>
</a> </article>
</li> </a>
</li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@@ -1,13 +1,2 @@
from django.db import models as django_models from django.test import TestCase
from django.contrib.auth.models import User from django.contrib.auth.models import User
def mock_user():
explorer_list = User.objects.all()
if len(explorer_list) <= 0:
return User.objects.create_user(
username='testpeter',
password='Develop123'
)
else:
return explorer_list[0]

View File

@@ -1,58 +1,107 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User
from django.core.exceptions import FieldDoesNotExist
from django.test import TestCase
class TestModel: # Creating a test user
class ModelTestCase:
''' '''
Base class for Lostplaces models Base class for ModelTests
''' '''
model = None
model_name = None model_name = None
def _test_field(self, field_name, field_class): def setUp(self):
if not self.model._meta.abstract:
self.object = self.model.objects.get(id=1)
self.model_name = self.model.__name__
def _test_field(self, field_name, field_class, must_have={}, must_not_have={}):
''' '''
Tests if a field exists under the given name and Tests if a field exists under the given name and
if the field is of the right type if the field is of the right type.
Also checks if the field has the given must_have attributes
and does not have any of the must_not_have attributes. If you
dont care about the value of the attribute you can just set it to
something that fullfills value == False (i.e. '' or 0)
''' '''
field = self.object._meta.get_field(field_name) try:
self.assertTrue( field = self.model._meta.get_field(field_name)
field, except FieldDoesNotExist:
msg="%s has no field named '%s'" % ( self.fail(
self.model_name, 'Expecting %s to have a field named \'%s\'' % (
field_name self.model_name,
field_name
)
) )
)
self.assertEqual( self.assertEqual(
type(field), field_class, type(field), field_class,
msg='%s.%s name field is no CharField' % ( msg='Expecting type of %s.%s to be %s' % (
self.model_name, self.model_name,
field_name field_name,
field_class.__name__
) )
) )
for key, value in must_have.items():
if value:
self.assertEqual(getattr(field, key), value,
msg='Expeting %s.%s.%s to be \'%s\'' % (
self.model_name,
field_name,
key,
value
)
)
else:
self.assertTrue(hasattr(field, key),
msg='Expeting %s.%s to have \'%s\'' % (
self.model_name,
field_name,
key
)
)
for key, value in must_not_have.items():
if value:
self.assertTrue(getattr(field, key) != value,
msg='Expeting %s.%s.%s to not be \'%s\'' % (
self.model_name,
field_name,
key,
value
)
)
else:
self.assertFalse(hasattr(field, value),
msg='Expeting %s.%s to not have \'%s\'' % (
self.model_name,
field_name,
key
)
)
return field return field
def _test_char_field(self, field_name, min_length, max_length, must_have={}, must_hot_have={}):
def _test_char_field(self, field_name, min_length, max_length):
''' '''
Tests if the given field is a char field and if its max_length Tests if the given field is a char field and if its max_length
is in min_length and max_legth is in min_length and max_legth
''' '''
field = self._test_field(field_name, models.CharField) field = self._test_field(
self.assertEqual( field_name, models.CharField, must_have, must_hot_have)
type(field), models.CharField,
msg='%s.%s name field is no CharField' % (
self.model_name,
field_name
)
)
self.assertTrue( self.assertTrue(
field.max_length in range(min_length, max_length), field.max_length in range(min_length, max_length),
msg='%s.%s field max_length not in range of %d and %d' % ( msg='Expeting %s.%s field max_length to be in the range of %d and %d' % (
self.model_name, self.model_name,
field_name, field_name,
min_length, min_length,
max_length max_length
) )
) )
def _test_float_field(self, field_name, min_value=None, max_value=None): def _test_float_field(self, field_name, min_value=None, max_value=None, must_have={}, must_hot_have={}):
''' '''
Tests if the field is a floatfield. If min_value and/or max_value are passed, Tests if the field is a floatfield. If min_value and/or max_value are passed,
the validators of the field are also checked. The validator list of the field should the validators of the field are also checked. The validator list of the field should
@@ -61,21 +110,23 @@ class TestModel:
[MinValueValidator] if only min_value is passed, [MinValueValidator] if only min_value is passed,
[MaxValueValidator] if only max_value is passed [MaxValueValidator] if only max_value is passed
''' '''
field = self._test_field(field_name, models.FloatField) field = self._test_field(
field_name, models.FloatField, must_have, must_hot_have)
if min_value: if min_value:
self.assertTrue( self.assertTrue(
len(field.validators) >= 1, len(field.validators) >= 1,
msg='%s.%s first validator should check minimum' % ( msg='Expecting the first valiator of %s.%s to check the minimum' % (
self.model_name, self.model_name,
field_name field_name
)
) )
)
self.assertEqual( self.assertEqual(
field.validators[0].limit_value, field.validators[0].limit_value,
min_value, min_value,
msg='%s.%s min value missmatch' % ( msg='Expecting the min value of %s.%s min to be at least %d' % (
self.model_name, self.model_name,
field_name field_name,
min_value
) )
) )
if max_value: if max_value:
@@ -84,38 +135,40 @@ class TestModel:
index += 1 index += 1
self.assertTrue( self.assertTrue(
len(field.validators) >= index+1, len(field.validators) >= index+1,
msg='%s.%s second validator should check maximum' % ( msg='Expecting the second valiator of %s.%s to check the maximum' % (
self.model_name,
field_name
)
)
self.assertEqual(
field.validators[1].limit_value,
max_value,
msg='%s.%s max value missmatch' % (
self.model_name, self.model_name,
field_name field_name
) )
) )
self.assertEqual(
field.validators[1].limit_value,
max_value,
msg='Expecting the max value of %s.%s min to be at most %d' % (
self.model_name,
field_name,
max_value
)
)
class TestSubmittable(TestModel):
model_name='<Class>' class SubmittableTestCase(ModelTestCase):
related_name = None related_name = None
nullable = False nullable = False
def test_submitted_when(self): def test_submitted_when(self):
submitted_when = self._test_field('submitted_when', models.DateTimeField) submitted_when = self._test_field(
self.assertTrue(submitted_when.auto_now_add, 'submitted_when',
msg='%s.submitted_when should be auto_now_add' % ( models.DateTimeField,
self.model_name must_have={'auto_now_add': True}
)
) )
def test_submitted_by(self): def test_submitted_by(self):
submitted_by = self._test_field('submitted_by',models.ForeignKey) submitted_by = self._test_field('submitted_by', models.ForeignKey)
if self.related_name: if self.related_name:
self.assertEqual(submitted_by.remote_field.related_name, self.related_name) self.assertEqual(
submitted_by.remote_field.related_name, self.related_name)
if self.nullable: if self.nullable:
self.assertTrue(submitted_by.null,) self.assertTrue(submitted_by.null,)
self.assertTrue(submitted_by.blank) self.assertTrue(submitted_by.blank)
self.assertEqual(submitted_by.remote_field.on_delete, models.SET_NULL) self.assertEqual(
submitted_by.remote_field.on_delete, models.SET_NULL)

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -0,0 +1,44 @@
import datetime
from django.test import TestCase
from django.db import models
from django.contrib.auth.models import User
from lostplaces_app.models import Place, Taggable, MapablePoint
from lostplaces_app.tests.models import ModelTestCase
from taggit.managers import TaggableManager
class TaggableTestCase(ModelTestCase, TestCase):
model = Taggable
def test_tags(self):
self._test_field('tags', TaggableManager)
class MapablePointTestCase(ModelTestCase, TestCase):
model = MapablePoint
def test_name(self):
self._test_char_field(
field_name='name',
min_length=10,
max_length=100
)
def test_latitude(self):
self._test_float_field(
field_name='latitude',
min_value=-90,
max_value=90
)
def test_longitude(self):
self._test_float_field(
field_name='longitude',
min_value=-180,
max_value=180
)

View File

@@ -0,0 +1,58 @@
from django.test import TestCase
from django.db import models
from django.contrib.auth.models import User
from lostplaces_app.models import Explorer
class ExplorerTestCase(TestCase):
@classmethod
def setUpTestData(self):
User.objects.create_user(
username='testpeter',
password='Develop123'
)
def test_epxlorer_creation(self):
'''
Tests if the explorer profile will be automticly
created when a user is created
'''
user = User.objects.get(id=1)
explorer_list = Explorer.objects.all()
self.assertTrue(len(explorer_list) > 0,
msg='Expecting at least one Exlorer object, none found'
)
self.assertTrue(hasattr(user, 'explorer'),
msg='''Expecting the User instance to have an \'explorer\' attribute.
Check the Explorer model and the related name.'''
)
explorer = Explorer.objects.get(id=1)
self.assertEqual(explorer, user.explorer,
msg='''The Explorer object of the User did not match.
Expecting User with id 1 to have Explorer with id 1'''
)
explorer = Explorer.objects.get(id=1)
self.assertEqual(explorer.user, user,
msg='''The User object of the Explorer did not match.
Expecting Explorer with id 1 to have User with id 1'''
)
def test_explorer_deletion(self):
'''
Tests if the Explorer objects get's deleted when the User instance is deleted
'''
user = User.objects.get(username='testpeter')
explorer_id = user.explorer.id
user.delete()
with self.assertRaises(models.ObjectDoesNotExist,
msg='Expecting explorer objec to be deleted when the corresponding User object is deleted'
):
Explorer.objects.get(id=explorer_id)

View File

@@ -1,32 +1,63 @@
import datetime import datetime
import os
import shutil
from unittest import mock from unittest import mock
from django.test import TestCase from django.test import TestCase
from django.db import models from django.db import models
from django.core.files import File from django.core.files import File
from django.conf import settings
from django.contrib.auth.models import User
from lostplaces_app.models import PlaceImage from lostplaces_app.models import PlaceImage, Place
from lostplaces_app.tests.models import TestSubmittable from lostplaces_app.tests.models import SubmittableTestCase
from lostplaces_app.tests import mock_user
from lostplaces_app.tests.models import TestModel
from lostplaces_app.tests.models.test_place_model import mock_place
from easy_thumbnails.fields import ThumbnailerImageField from easy_thumbnails.fields import ThumbnailerImageField
def mock_place_image(): class TestPlaceImage(SubmittableTestCase, TestCase):
return PlaceImage( model = PlaceImage
description='Im a description',
filename=mock.MagicMock(spec=File, name='FileMock'),
place=mock_place(),
submitted_when=datetime.datetime.now(),
submitted_by=mock_user().explorer
)
class TestPlaceImage(TestSubmittable, TestCase): @classmethod
model_name = 'PlaceImage' def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123'
)
def setUp(self): place = Place.objects.create(
self.object = mock_place_image() name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=User.objects.get(username='testpeter').explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
if not os.path.isdir(settings.MEDIA_ROOT):
os.mkdir(settings.MEDIA_ROOT)
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')):
shutil.copyfile(
os.path.join(current_dir, 'im_a_image.jpeg'),
os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg')
)
shutil.copyfile(
os.path.join(current_dir, 'im_a_image.jpeg'),
os.path.join(settings.MEDIA_ROOT, 'im_a_image_changed.jpeg')
)
PlaceImage.objects.create(
description='Im a description',
filename=os.path.join(settings.MEDIA_ROOT, 'im_a_image_copy.jpeg'),
place=place,
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer
)
def test_description(self): def test_description(self):
self._test_field('description', models.TextField) self._test_field('description', models.TextField)
@@ -51,5 +82,26 @@ class TestPlaceImage(TestSubmittable, TestCase):
) )
def test_str(self): def test_str(self):
place_image = mock_place_image() place_image = self.object
self.assertEqual(str(place_image), ' '.join([place_image.place.name, str(place_image.pk)])) self.assertTrue(place_image.place.name.lower() in str(place_image).lower(),
msg='Expecting %s.__str__ to contain the name of the place' % (
self.model_name
)
)
def test_change_filename(self):
path = self.object.filename.path
self.object.filename = os.path.join(settings.MEDIA_ROOT, 'im_a_image_changed.jpeg')
self.object.save()
self.assertFalse(
os.path.isfile(path),
msg='Expecting the old file of an place_image to be deleteed when an place_image file is changed'
)
def test_deletion(self):
path = self.object.filename.path
self.object.delete()
self.assertFalse(
os.path.isfile(path),
msg='Expecting the file of an place_image to be deleteed when an place_image is deleted'
)

View File

@@ -3,43 +3,36 @@ 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 lostplaces_app.models import Place from lostplaces_app.models import Place
from lostplaces_app.tests.models import TestSubmittable from lostplaces_app.tests.models import SubmittableTestCase
from lostplaces_app.tests import mock_user
from lostplaces_app.tests.models import TestModel
from taggit.managers import TaggableManager class PlaceTestCase(SubmittableTestCase, TestCase):
model = Place
def mock_place():
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=mock_user().explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
return place
class PlaceTestCase(TestSubmittable, TestCase):
model_name = 'Place'
related_name = 'places' related_name = 'places'
nullable = True nullable = True
def setUp(self): @classmethod
self.place = mock_place() def setUpTestData(cls):
self.object = self.place user = User.objects.create_user(
username='testpeter',
def test_name_field(self): password='Develop123'
self._test_char_field(
field_name='name',
min_length=10,
max_length=100
) )
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def test_location(self): def test_location(self):
self._test_char_field( self._test_char_field(
field_name='location', field_name='location',
@@ -47,26 +40,9 @@ class PlaceTestCase(TestSubmittable, TestCase):
max_length=100 max_length=100
) )
def test_latitude(self):
self._test_float_field(
field_name='latitude',
min_value=-90,
max_value=90
)
def test_longitude(self):
self._test_float_field(
field_name='longitude',
min_value=-180,
max_value=180
)
def test_decsription(self): def test_decsription(self):
self._test_field('description', models.TextField) self._test_field('description', models.TextField)
def test_tags(self):
self._test_field('tags', TaggableManager)
def test_average_latlon(self): def test_average_latlon(self):
''' '''
Tests the average latitude/longitude calculation of a list Tests the average latitude/longitude calculation of a list
@@ -74,18 +50,28 @@ class PlaceTestCase(TestSubmittable, TestCase):
''' '''
place_list = [] place_list = []
for i in range(10): for i in range(10):
place = mock_place() place = Place.objects.get(id=1)
place.id = None
place.latitude = i+1 place.latitude = i+1
place.longitude = i+10 place.longitude = i+10
place.save()
place_list.append(place) place_list.append(place)
avg_latlon = Place.average_latlon(place_list) avg_latlon = Place.average_latlon(place_list)
self.assertEqual(avg_latlon[0], 5.5,
self.assertTrue('latitude' in avg_latlon,
msg='Expecting avg_latlon dict to have an \'latitude\' key'
)
self.assertTrue('longitude' in avg_latlon,
msg='Expecting avg_latlon dict to have an \'longitude\' key'
)
self.assertEqual(avg_latlon['latitude'], 5.5,
msg='%s: average latitude missmatch' % ( msg='%s: average latitude missmatch' % (
self.model_name self.model_name
) )
) )
self.assertEqual(avg_latlon[1], 14.5, self.assertEqual(avg_latlon['longitude'], 14.5,
msg='%s: average longitude missmatch' % ( msg='%s: average longitude missmatch' % (
self.model_name self.model_name
) )
@@ -96,14 +82,14 @@ class PlaceTestCase(TestSubmittable, TestCase):
Tests the average latitude/longitude calculation of a list Tests the average latitude/longitude calculation of a list
of one place of one place
''' '''
place = mock_place() place = Place.objects.get(id=1)
avg_latlon = Place.average_latlon([place]) avg_latlon = Place.average_latlon([place])
self.assertEqual(avg_latlon[0], place.latitude, self.assertEqual(avg_latlon['latitude'], place.latitude,
msg='%s:(one place) average latitude missmatch' % ( msg='%s:(one place) average latitude missmatch' % (
self.model_name self.model_name
) )
) )
self.assertEqual(avg_latlon[1], place.longitude, self.assertEqual(avg_latlon['longitude'], place.longitude,
msg='%s: (one place) average longitude missmatch' % ( msg='%s: (one place) average longitude missmatch' % (
self.model_name self.model_name
) )
@@ -115,22 +101,21 @@ class PlaceTestCase(TestSubmittable, TestCase):
an empty list an empty list
''' '''
avg_latlon = Place.average_latlon([]) avg_latlon = Place.average_latlon([])
self.assertEqual(avg_latlon[0], 0, self.assertEqual(avg_latlon['latitude'], 0,
msg='%s: (no places) average latitude missmatch' % ( msg='%s: (no places) average latitude missmatch' % (
self.model_name self.model_name
) )
) )
self.assertEqual(avg_latlon[1], 0, self.assertEqual(avg_latlon['longitude'], 0,
msg='%s: a(no places) verage longitude missmatch' % ( msg='%s: a(no places) verage longitude missmatch' % (
self.model_name self.model_name
) )
) )
def test_str(self): def test_str(self):
place = mock_place() place = self.object
self.assertEqual(str(place), place.name, self.assertTrue(place.name.lower() in str(place).lower(),
msg='%s __str__ should return the name' % ( msg='Expecting %s.__str__ to contain the name' % (
self.model_name self.model_name
) )
) )

View File

@@ -0,0 +1,49 @@
import datetime
from django.test import TestCase
from django.db import models
from django.utils import timezone
from lostplaces_app.models import Voucher
from lostplaces_app.tests.models import ModelTestCase
class VoucheTestCase(ModelTestCase, TestCase):
model = Voucher
@classmethod
def setUpTestData(cls):
Voucher.objects.create(
code='ayDraJCCwfhcFiYmSR5GrcjcchDfcahv',
expires_when=timezone.now() + datetime.timedelta(days=1)
)
def test_voucher_code(self):
self._test_char_field(
'code',
10,
100,
must_have={'unique': True}
)
def test_voucher_created(self):
self._test_field(
'created_when',
models.DateTimeField,
must_have={'auto_now_add': True}
)
def test_voucher_expires(self):
self._test_field(
'expires_when',
models.DateTimeField,
must_not_have={'auto_now_add': True}
)
def test_str(self):
voucher = self.object
self.assertTrue(voucher.code.lower() in str(voucher).lower(),
msg='Expecting %s.__str__ to contain the voucher code' % (
self.model_name
)
)

View File

@@ -0,0 +1,77 @@
from django.test import Client
class ViewTestCaseMixin:
'''
This is a mixni for testing views. It provides functionality to
test the context, forms and HTTP Response of responses.
Also works with django's ReqeustFactory.
'''
view = None
def setUp(self):
self.view_name = self.view.__name__
self. client = Client()
def assertHasContextKey(self, response, context_key):
self.assertTrue( context_key in response.context,
msg='Expecting the context of %s to have an attribute \'%s\'' % (
self.view_name,
context_key
)
)
def assertHasForm(self, response, context_key, form_class):
self.assertHasContextKey(response, context_key)
self.assertEqual(
type(response.context[context_key]),
form_class,
msg='Expecting %s\'s context.%s to be of the type %s' % (
self.view_name,
context_key,
form_class.__name__
)
)
def assertHttpCode(self, response, code):
self.assertEqual(
response.status_code, code,
"Expected an HTTP %s response, but got HTTP %s" % (
code,
response.status_code
)
)
def assertHttpOK(self, response):
self.assertHttpCode(response, 200)
def assertHttpCreated(self, response):
self.assertHttpCode(response, 201)
def assertHttpRedirect(self, response, redirect_to):
'''
Assert that we had any redirect status code.
'''
self.assertTrue(
300 <= response.status_code < 400,
'Expected an HTTP 3XX (redirect) response, but got HTTP %s' %
response.status_code
)
self.assertEqual(response['Location'], redirect_to)
def assertHttpBadRequest(self, response):
self.assertHttpCode(response, 400)
def assertHttpUnauthorized(self, response):
self.assertHttpCode(response, 401)
def assertHttpForbidden(self, response):
self.assertHttpCode(response, 403)
def assertHttpNotFound(self, response):
self.assertHttpCode(response, 404)
def assertHttpMethodNotAllowed(self, response):
self.assertHttpCode(response, 405)

View File

@@ -1,45 +1,65 @@
from django.test import TestCase, Client import datetime
from django.test import TestCase, RequestFactory, Client
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.auth.models import User, AnonymousUser
from django.contrib.messages.storage.fallback import FallbackStorage
from lostplaces_app.models import Place from lostplaces_app.models import Place
from lostplaces_app.views import IsAuthenticatedMixin
from django.contrib.auth.models import User class TestIsAuthenticatedMixin(RedirectTestCase):
from lostplaces_app.tests.models.test_place_model import mock_place
from lostplaces_app.tests import mock_user
class TestIsAuthenticated(TestCase): @classmethod
def setUp(self): def setUpTestData(cls):
self. client = Client() user = User.objects.create_user(
mock_place() username='testpeter',
mock_user() password='Develop123'
)
def test_logged_in(self): def test_logged_in(self):
self.client.login(username='testpeter', password='Develop123') request = RequestFactory().get('/')
response = self.client.get(reverse_lazy('place_detail', kwargs={'pk': 1})) request.user = User.objects.get(id=1)
response = IsAuthenticatedMixin.as_view()(request)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_not_logged_in(self): def test_not_logged_in(self):
url = reverse_lazy('place_detail', kwargs={'pk': 1}) request = RequestFactory().get('/someurl1234')
response = self.client.get(url, follow=True) request.user = AnonymousUser()
self.assertRedirects( request.session = 'session'
response=response, messages = FallbackStorage(request)
expected_url='?'.join([str(reverse_lazy('login')), 'next=/place/1/']), request._messages = messages
status_code=302,
target_status_code=200, response = IsAuthenticatedMixin.as_view()(request)
msg_prefix='''Accesing an IsAuthenticated view while not logged should self.assertRedirectsTo(response, '?'.join([str(reverse_lazy('login')), 'next=/someurl1234']))
redirect to login page with redirect params
''',
fetch_redirect_response=True
)
self.assertTrue(response.context['messages']) self.assertTrue(response.context['messages'])
self.assertTrue(len(response.context['messages']) > 0) self.assertTrue(len(response.context['messages']) > 0)
class TestIsPlaceSubmitter(TestCase): class TestIsPlaceSubmitterMixin(TestCase):
@classmethod
def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123'
)
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def setUp(self): def setUp(self):
self. client = Client() self. client = Client()
mock_place()
mock_user()
def test_is_submitter(self): def test_is_submitter(self):
self.client.login(username='testpeter', password='Develop123') self.client.login(username='testpeter', password='Develop123')

View File

@@ -1,35 +1,73 @@
from django.test import TestCase, Client import datetime
from django.test import TestCase
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.auth.models import User
from lostplaces_app.models import Place from lostplaces_app.models import Place
from lostplaces_app.views import (
PlaceCreateView,
PlaceListView
)
from lostplaces_app.forms import PlaceImageCreateForm, PlaceForm
from lostplaces_app.tests.views import ViewTestCaseMixin
from lostplaces_app.tests.models.test_place_model import mock_place class TestPlaceCreateView(ViewTestCaseMixin, TestCase):
from lostplaces_app.tests import mock_user
class TestPlaceCreateView(TestCase): view = PlaceCreateView
def setUp(self): @classmethod
self. client = Client() def setUpTestData(cls):
mock_place() user = User.objects.create_user(
mock_user() username='testpeter',
password='Develop123'
def test_url_logged_in(self):
self.client.login(username='testpeter', password='Develop123')
response = self.client.get(reverse_lazy('place_detail', kwargs={'pk': 1}))
self.assertEqual(response.status_code, 200)
def test_url_not_logged_in(self):
url = reverse_lazy('place_detail', kwargs={'pk': 1})
response = self.client.get(url)
self.assertRedirects(
response=response,
expected_url='?'.join([str(reverse_lazy('login')), 'next=/place/1/']),
status_code=302,
target_status_code=200,
msg_prefix='''Accesing PlaceDetailView while not logged should
redirect to login page with redirect params
''',
fetch_redirect_response=True
) )
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def test_has_forms(self):
self.client.login(username='testpeter', password='Develop123')
response = self.client.get(reverse_lazy('place_create'))
self.assertHasForm(response, 'place_image_form', PlaceImageCreateForm)
self.assertHasForm(response, 'place_form', PlaceForm)
class TestPlaceListView(ViewTestCaseMixin, TestCase):
view = PlaceListView
@classmethod
def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123'
)
place = Place.objects.create(
name='Im a place',
submitted_when=datetime.datetime.now(),
submitted_by=user.explorer,
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
)
place.tags.add('I a tag', 'testlocation')
place.save()
def test_list_view(self):
self.client.login(username='testpeter', password='Develop123')
response = self.client.get(reverse_lazy('place_list'))
self.assertHasContextKey(response, 'map_config')

View File

@@ -1,5 +1,5 @@
from django.urls import path from django.urls import path
from .views import ( from lostplaces_app.views import (
HomeView, HomeView,
PlaceDetailView, PlaceDetailView,
PlaceListView, PlaceListView,
@@ -25,6 +25,6 @@ urlpatterns = [
path('flat/<slug:slug>/', FlatView, name='flatpage'), path('flat/<slug:slug>/', FlatView, name='flatpage'),
# POST-only URLs for tag submission # POST-only URLs for tag submission
path('place/tag/<int:place_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'), path('place/tag/<int:tagged_id>', PlaceTagSubmitView.as_view(), name='place_tag_submit'),
path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete') path('place/tag/delete/<int:tagged_id>/<int:tag_id>', PlaceTagDeleteView.as_view(), name='place_tag_delete')
] ]

View File

@@ -11,7 +11,7 @@ from django.urls import reverse_lazy
from lostplaces_app.models import Place from lostplaces_app.models import Place
class IsAuthenticated(LoginRequiredMixin, View): class IsAuthenticatedMixin(LoginRequiredMixin, View):
''' '''
A view mixin that checks wether a user is loged in or not. A view mixin that checks wether a user is loged in or not.
If the user is not logged in, he gets redirected to If the user is not logged in, he gets redirected to
@@ -24,7 +24,7 @@ class IsAuthenticated(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 IsPlaceSubmitter(UserPassesTestMixin, View): class IsPlaceSubmitterMixin(UserPassesTestMixin, View):
''' '''
A view mixin that checks wethe a user is the submitter A view mixin that checks wethe a user is the submitter
of a place Throws 403 if the user is not. The subclass of a place Throws 403 if the user is not. The subclass
@@ -55,7 +55,7 @@ class IsPlaceSubmitter(UserPassesTestMixin, View):
messages.error(self.request, self.place_submitter_error_message) messages.error(self.request, self.place_submitter_error_message)
return False return False
class PlaceAssetCreateView(IsAuthenticated, SuccessMessageMixin, CreateView): class PlaceAssetCreateView(IsAuthenticatedMixin, SuccessMessageMixin, CreateView):
model = None model = None
fields = [] fields = []
template_name = '' template_name = ''
@@ -81,7 +81,7 @@ class PlaceAssetCreateView(IsAuthenticated, SuccessMessageMixin, CreateView):
def get_success_url(self): def get_success_url(self):
return reverse_lazy('place_detail', kwargs={'pk': self.place.id}) return reverse_lazy('place_detail', kwargs={'pk': self.place.id})
class PlaceAssetDeleteView(IsAuthenticated, IsPlaceSubmitter, SingleObjectMixin, View): class PlaceAssetDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, SingleObjectMixin, View):
model = None model = None
success_message = '' success_message = ''
permission_denied_message = '' permission_denied_message = ''

View File

@@ -10,12 +10,12 @@ from django.shortcuts import render, redirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from lostplaces_app.models import Place, PlaceImage from lostplaces_app.models import Place, PlaceImage
from lostplaces_app.views import IsAuthenticated, IsPlaceSubmitter from lostplaces_app.views import IsAuthenticatedMixin, IsPlaceSubmitterMixin
from lostplaces_app.forms import PlaceForm, PlaceImageCreateForm, TagSubmitForm from lostplaces_app.forms import PlaceForm, PlaceImageCreateForm, TagSubmitForm
from taggit.models import Tag from taggit.models import Tag
class PlaceListView(IsAuthenticated, ListView): class PlaceListView(IsAuthenticatedMixin, ListView):
paginate_by = 5 paginate_by = 5
model = Place model = Place
template_name = 'place/place_list.html' template_name = 'place/place_list.html'
@@ -23,27 +23,32 @@ class PlaceListView(IsAuthenticated, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['place_map_center'] = Place.average_latlon(context['place_list']) context['map_config'] = {
'point_list': context['place_list'],
'map_center': Place.average_latlon(context['place_list'])
}
return context return context
class PlaceDetailView(IsAuthenticated, View): class PlaceDetailView(IsAuthenticatedMixin, View):
def get(self, request, pk): def get(self, request, pk):
place = Place.objects.get(pk=pk) place = Place.objects.get(pk=pk)
context = { context = {
'place': place, 'place': place,
'place_list': [ place ], 'map_config': {
'place_map_center': [ place.latitude, place.longitude ], 'point_list': [ place ],
'all_tags': Tag.objects.all(), 'map_center': {'latitude': place.latitude, 'longitude': place.longitude},
},
'tagging_config': { 'tagging_config': {
'submit_url': reverse_lazy('place_tag_submit', kwargs={'place_id': place.id}), 'all_tags': Tag.objects.all(),
'submit_form': TagSubmitForm(), 'submit_form': TagSubmitForm(),
'tagged_item': place, 'tagged_item': place,
'submit_url_name': 'place_tag_submit',
'delete_url_name': 'place_tag_delete' 'delete_url_name': 'place_tag_delete'
} }
} }
return render(request, 'place/place_detail.html', context) return render(request, 'place/place_detail.html', context)
class PlaceUpdateView(IsAuthenticated, IsPlaceSubmitter, SuccessMessageMixin, UpdateView): class PlaceUpdateView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, SuccessMessageMixin, UpdateView):
template_name = 'place/place_update.html' template_name = 'place/place_update.html'
model = Place model = Place
form_class = PlaceForm form_class = PlaceForm
@@ -56,7 +61,7 @@ class PlaceUpdateView(IsAuthenticated, IsPlaceSubmitter, SuccessMessageMixin, Up
def get_place(self): def get_place(self):
return self.get_object() return self.get_object()
class PlaceCreateView(IsAuthenticated, View): class PlaceCreateView(IsAuthenticatedMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
place_image_form = PlaceImageCreateForm() place_image_form = PlaceImageCreateForm()
@@ -112,7 +117,7 @@ class PlaceCreateView(IsAuthenticated, View):
) )
place_image.save() place_image.save()
class PlaceDeleteView(IsAuthenticated, IsPlaceSubmitter, DeleteView): class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
template_name = 'place/place_delete.html' template_name = 'place/place_delete.html'
model = Place model = Place
success_message = 'Successfully deleted place.' success_message = 'Successfully deleted place.'

View File

@@ -9,7 +9,7 @@ from django.http import HttpResponseForbidden
from lostplaces_app.forms import ExplorerCreationForm, TagSubmitForm from lostplaces_app.forms import ExplorerCreationForm, TagSubmitForm
from lostplaces_app.models import Place, PhotoAlbum from lostplaces_app.models import Place, PhotoAlbum
from lostplaces_app.views.base_views import IsAuthenticated from lostplaces_app.views.base_views import IsAuthenticatedMixin
from lostplaces_app.views.base_views import ( from lostplaces_app.views.base_views import (
PlaceAssetCreateView, PlaceAssetCreateView,
@@ -24,13 +24,15 @@ class SignUpView(SuccessMessageMixin, CreateView):
template_name = 'signup.html' template_name = 'signup.html'
success_message = 'User created.' success_message = 'User created.'
class HomeView(IsAuthenticated, View): class HomeView(IsAuthenticatedMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
place_list = Place.objects.all().order_by('-submitted_when')[:10] place_list = Place.objects.all().order_by('-submitted_when')[:10]
place_map_center = Place.average_latlon(place_list)
context = { context = {
'place_list': place_list, 'place_list': place_list,
'place_map_center': place_map_center 'map_config': {
'point_list': place_list,
'map_center': Place.average_latlon(place_list)
}
} }
return render(request, 'home.html', context) return render(request, 'home.html', context)
@@ -53,9 +55,9 @@ class PhotoAlbumDeleteView(PlaceAssetDeleteView):
success_message = 'Photo Album deleted' success_message = 'Photo Album deleted'
permission_denied_messsage = 'You do not have permissions to alter this photo album' permission_denied_messsage = 'You do not have permissions to alter this photo album'
class PlaceTagSubmitView(IsAuthenticated, View): class PlaceTagSubmitView(IsAuthenticatedMixin, View):
def post(self, request, place_id, *args, **kwargs): def post(self, request, tagged_id, *args, **kwargs):
place = Place.objects.get(pk=place_id) place = Place.objects.get(pk=tagged_id)
form = TagSubmitForm(request.POST) form = TagSubmitForm(request.POST)
if form.is_valid(): if form.is_valid():
tag_list_raw = form.cleaned_data['tag_list'] tag_list_raw = form.cleaned_data['tag_list']
@@ -68,7 +70,7 @@ class PlaceTagSubmitView(IsAuthenticated, View):
return redirect(reverse_lazy('place_detail', kwargs={'pk': place.id})) return redirect(reverse_lazy('place_detail', kwargs={'pk': place.id}))
class PlaceTagDeleteView(IsAuthenticated, View): class PlaceTagDeleteView(IsAuthenticatedMixin, View):
def get(self, request, tagged_id, tag_id, *args, **kwargs): def get(self, request, tagged_id, tag_id, *args, **kwargs):
place = Place.objects.get(pk=tagged_id) place = Place.objects.get(pk=tagged_id)
tag = Tag.objects.get(pk=tag_id) tag = Tag.objects.get(pk=tag_id)