Compare commits
	
		
			59 Commits
		
	
	
		
			0.1.2
			...
			c8b3cff5a6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c8b3cff5a6 | |||
| 547179b0ca | |||
| 0765f6606f | |||
| 12881d9345 | |||
| 79ed029db0 | |||
| 1a8da002cf | |||
| c828d04f05 | |||
| f919fe30fa | |||
| b3db6643b9 | |||
| cea3a909b5 | |||
| b52d96a55e | |||
| 1fb71a172e | |||
| 27520c7ca4 | |||
| af14cce3f8 | |||
| 75bcc91037 | |||
| c78ff60231 | |||
| 09eb8794b8 | |||
| 470e54da8d | |||
| 19299598c3 | |||
| f1c51ab8a7 | |||
| b77c5d1d7f | |||
| 05481fc0c8 | |||
| 6b00452830 | |||
| 7e4c5dcf24 | |||
| c0f30e56f7 | |||
| 9852646fff | |||
| c2d678847e | |||
| e77edf18ac | |||
| 3780aa6cf1 | |||
| 21124ec2ad | |||
| 0ee5fc59d3 | |||
| b8dfef691e | |||
| a1886b0b60 | |||
| e1002b5315 | |||
| fed90d4f7b | |||
| dcfb329c5a | |||
| b8a21a8baa | |||
| 7f73035b02 | |||
| 317437fedc | |||
| 26286984c2 | |||
| 9ae31c0146 | |||
| 87fd8fa96f | |||
| c3401e732f | |||
| 4ee7373b3f | |||
| 64c0c5f8e6 | |||
| 18a597c726 | |||
| 
						 | 
					baca596603 | ||
| d993387216 | |||
| aed2856df3 | |||
| c78858c152 | |||
| f49581259e | |||
| f5bf642cd6 | |||
| 7687acb366 | |||
| e655e1598a | |||
| 64ed38332f | |||
| d438303aec | |||
| 38b3736951 | |||
| 6be060ea40 | |||
| 5c5756150f | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -69,7 +69,7 @@ coverage.xml
 | 
				
			|||||||
# exclude migrations from repository. These should be created locally, matching local DB requirements.
 | 
					# exclude migrations from repository. These should be created locally, matching local DB requirements.
 | 
				
			||||||
# lostplaces/manage.py makemigrations && lostplaces/manage.py migrate
 | 
					# lostplaces/manage.py makemigrations && lostplaces/manage.py migrate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lostplaces/lostplaces_app/migrations/
 | 
					django_lostplaces/lostplaces/migrations/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# pyenv
 | 
					# pyenv
 | 
				
			||||||
.python-version
 | 
					.python-version
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
include LICENSE
 | 
					include LICENSE
 | 
				
			||||||
include Readme.rst
 | 
					include Readme.rst
 | 
				
			||||||
include Pipfile
 | 
					include Pipfile
 | 
				
			||||||
recursive-include lostplaces_app/static *
 | 
					recursive-include lostplaces/static *
 | 
				
			||||||
recursive-include lostplaces_app/templates *
 | 
					recursive-include lostplaces/templates *
 | 
				
			||||||
							
								
								
									
										11
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						@@ -11,6 +11,7 @@ pipenv = "*"
 | 
				
			|||||||
wheel = "*"
 | 
					wheel = "*"
 | 
				
			||||||
twine = "*"
 | 
					twine = "*"
 | 
				
			||||||
pandoc ="*"
 | 
					pandoc ="*"
 | 
				
			||||||
 | 
					pylint-django ="*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[packages]
 | 
					[packages]
 | 
				
			||||||
django = "*"
 | 
					django = "*"
 | 
				
			||||||
@@ -18,6 +19,10 @@ easy-thumbnails = "*"
 | 
				
			|||||||
image = "*"
 | 
					image = "*"
 | 
				
			||||||
django-widget-tweaks = "*"
 | 
					django-widget-tweaks = "*"
 | 
				
			||||||
django-taggit = "*"
 | 
					django-taggit = "*"
 | 
				
			||||||
# Commented out to not explicitly specify Python 3 subversion.
 | 
					
 | 
				
			||||||
# [requires]
 | 
					[scripts]
 | 
				
			||||||
# python_version = "3.8"
 | 
					test = "django_lostplaces/manage.py test lostplaces"
 | 
				
			||||||
 | 
					server = "django_lostplaces/manage.py runserver --ipv6"
 | 
				
			||||||
 | 
					dbshell = "django_lostplaces/manage.py dbshell"
 | 
				
			||||||
 | 
					showmigrations "django_lostplaces/manage.py showmigrations"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										38
									
								
								Readme.md
									
									
									
									
									
								
							
							
						
						@@ -34,10 +34,10 @@ After having obtained the repository contents (either via .zip download or git c
 | 
				
			|||||||
$ cd lostplaces-backend
 | 
					$ cd lostplaces-backend
 | 
				
			||||||
$ pipenv install 
 | 
					$ pipenv install 
 | 
				
			||||||
$ pipenv shell
 | 
					$ pipenv shell
 | 
				
			||||||
(lostplaces-backend) $ lostplaces/manage.py makemigrations
 | 
					(lostplaces-backend) $ django_lostplaces/manage.py makemigrations
 | 
				
			||||||
(lostplaces-backend) $ lostplaces/manage.py migrate
 | 
					(lostplaces-backend) $ django_lostplaces/manage.py migrate
 | 
				
			||||||
(lostplaces-backend) $ lostplaces/manage.py createsuperuser
 | 
					(lostplaces-backend) $ django_lostplaces/manage.py createsuperuser
 | 
				
			||||||
(lostplaces-backend) $ lostplaces/manage.py runserver --ipv6
 | 
					(lostplaces-backend) $ django_lostplaces/manage.py runserver --ipv6
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Returning to the venv
 | 
					## Returning to the venv
 | 
				
			||||||
@@ -45,9 +45,9 @@ $ pipenv shell
 | 
				
			|||||||
$ cd lostplaces-backend
 | 
					$ cd lostplaces-backend
 | 
				
			||||||
$ pipenv shell
 | 
					$ pipenv shell
 | 
				
			||||||
(lostplaces-backend) $ pipenv update # If dependencies changed, or updates available
 | 
					(lostplaces-backend) $ pipenv update # If dependencies changed, or updates available
 | 
				
			||||||
(lostplaces-backend) $ lostplaces/manage.py makemigrations # If datamodels changed
 | 
					(lostplaces-backend) $ django_lostplaces/manage.py makemigrations # If datamodels changed
 | 
				
			||||||
(lostplaces-backend) $ lostplaces/manage.py migrate # If datamodels changed
 | 
					(lostplaces-backend) $ django_lostplaces/manage.py migrate # If datamodels changed
 | 
				
			||||||
(lostplaces-backend) $ lostplaces/manage.py runserver --ipv6
 | 
					(lostplaces-backend) $ django_lostplaces/manage.py runserver --ipv6
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Visit: [admin](http://localhost:8000/admin) for administrative backend or
 | 
					Visit: [admin](http://localhost:8000/admin) for administrative backend or
 | 
				
			||||||
@@ -72,12 +72,12 @@ Before making the django instance public, you should tweak the config `settings.
 | 
				
			|||||||
2. Turn off debug mode by setting `DEBUG = False`.
 | 
					2. Turn off debug mode by setting `DEBUG = False`.
 | 
				
			||||||
3. Tune the localization settings, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/).
 | 
					3. Tune the localization settings, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Run `lostplaces/managy.py collectstatic` and you should be ready to go.
 | 
					Run `django_lostplaces/managy.py collectstatic` and you should be ready to go.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Installing the lostplaces_app to an existing django instance
 | 
					## Installing lostplaces to an existing django instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Installing django and the lostplaces app
 | 
					### Installing django and the django_lostplaces app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you haven't already setup a django instance, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/install/).
 | 
					If you haven't already setup a django instance, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/install/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -93,7 +93,7 @@ Now configure your `settings.py` as follows:
 | 
				
			|||||||
```python
 | 
					```python
 | 
				
			||||||
INSTALLED_APPS = [
 | 
					INSTALLED_APPS = [
 | 
				
			||||||
	...
 | 
						...
 | 
				
			||||||
	'lostplaces_app',
 | 
						'django_lostplaces',
 | 
				
			||||||
	'easy_thumbnails',
 | 
						'easy_thumbnails',
 | 
				
			||||||
	'widget_tweaks',
 | 
						'widget_tweaks',
 | 
				
			||||||
	'django_taggit'
 | 
						'django_taggit'
 | 
				
			||||||
@@ -110,18 +110,12 @@ MEDIA_URL = '/uploads/'
 | 
				
			|||||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
 | 
					MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
3. Set the user model (this will be changed in the next release):
 | 
					3. Set the URL's for login, for example:
 | 
				
			||||||
 | 
					 | 
				
			||||||
```python
 | 
					 | 
				
			||||||
AUTH_USER_MODEL = 'lostplaces_app.Explorer'
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
4. Set the URL's for login, for example:
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
```python
 | 
					```python
 | 
				
			||||||
LOGIN_URL = reverse_lazy('login')
 | 
					LOGIN_URL = reverse_lazy('login')
 | 
				
			||||||
LOGIN_REDIRECT_URL = reverse_lazy('lostplaces_home')
 | 
					LOGIN_REDIRECT_URL = reverse_lazy('django_lostplaces_home')
 | 
				
			||||||
LOGOUT_REDIRECT_URL = reverse_lazy('lostplaces_home')
 | 
					LOGOUT_REDIRECT_URL = reverse_lazy('django_lostplaces_home')
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Configuring the URL's
 | 
					### Configuring the URL's
 | 
				
			||||||
@@ -131,7 +125,7 @@ urlpatterns = [
 | 
				
			|||||||
    path('admin/', admin.site.urls),
 | 
					    path('admin/', admin.site.urls),
 | 
				
			||||||
    path('signup/', SignUpView.as_view(), name='signup'), # If you want to use lostplaces' sign up view.
 | 
					    path('signup/', SignUpView.as_view(), name='signup'), # If you want to use lostplaces' sign up view.
 | 
				
			||||||
    path('explorers/', include('django.contrib.auth.urls')), # You can change the 'explorers/' to whatever you desire.
 | 
					    path('explorers/', include('django.contrib.auth.urls')), # You can change the 'explorers/' to whatever you desire.
 | 
				
			||||||
    path('', include('lostplaces_app.urls')), # In this configuration lostplaces will be at the top level of you website, change '' to 'lostplaces/', if you don't want this.
 | 
					    path('', include('django_lostplaces.urls')), # In this configuration django_lostplaces will be at the top level of you website, change '' to 'django_lostplaces/', if you don't want this.
 | 
				
			||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # So django can deliver user uploaded files.
 | 
					] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # So django can deliver user uploaded files.
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -140,4 +134,4 @@ Before making the django instance public, you should tweak the config `settings.
 | 
				
			|||||||
2. Turn off debug mode by setting `DEBUG = False`.
 | 
					2. Turn off debug mode by setting `DEBUG = False`.
 | 
				
			||||||
3. Tune the localization settings, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/).
 | 
					3. Tune the localization settings, see [django's documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Run `lostplaces/managy.py collectstatic` you should be ready to go.
 | 
					Run `django_lostplaces/managy.py collectstatic` you should be ready to go.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,6 @@ import os
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.core.asgi import get_asgi_application
 | 
					from django.core.asgi import get_asgi_application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lostplaces.settings')
 | 
					os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_lostplaces.settings')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
application = get_asgi_application()
 | 
					application = get_asgi_application()
 | 
				
			||||||
@@ -38,7 +38,7 @@ ALLOWED_HOSTS = ['localhost']
 | 
				
			|||||||
# Application definition
 | 
					# Application definition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
INSTALLED_APPS = [
 | 
					INSTALLED_APPS = [
 | 
				
			||||||
    'lostplaces_app',
 | 
					    'lostplaces',
 | 
				
			||||||
    'easy_thumbnails',
 | 
					    'easy_thumbnails',
 | 
				
			||||||
    'widget_tweaks',
 | 
					    'widget_tweaks',
 | 
				
			||||||
    'taggit',
 | 
					    'taggit',
 | 
				
			||||||
@@ -60,7 +60,7 @@ MIDDLEWARE = [
 | 
				
			|||||||
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
					    'django.middleware.clickjacking.XFrameOptionsMiddleware',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ROOT_URLCONF = 'lostplaces.urls'
 | 
					ROOT_URLCONF = 'django_lostplaces.urls'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEMPLATES = [
 | 
					TEMPLATES = [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -78,7 +78,7 @@ TEMPLATES = [
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WSGI_APPLICATION = 'lostplaces.wsgi.application'
 | 
					WSGI_APPLICATION = 'django_lostplaces.wsgi.application'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Database
 | 
					# Database
 | 
				
			||||||
@@ -23,11 +23,11 @@ from django.conf.urls.static import static
 | 
				
			|||||||
from django.urls import path, include
 | 
					from django.urls import path, include
 | 
				
			||||||
from django.views.generic.base import TemplateView
 | 
					from django.views.generic.base import TemplateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces_app.views import SignUpView
 | 
					from lostplaces.views import SignUpView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    path('admin/', admin.site.urls),
 | 
					    path('admin/', admin.site.urls),
 | 
				
			||||||
    path('signup/', SignUpView.as_view(), name='signup'),
 | 
					    path('signup/', SignUpView.as_view(), name='signup'),
 | 
				
			||||||
    path('explorers/', include('django.contrib.auth.urls')),
 | 
					    path('explorers/', include('django.contrib.auth.urls')),
 | 
				
			||||||
    path('', include('lostplaces_app.urls')),
 | 
					    path('', include('lostplaces.urls')),
 | 
				
			||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 | 
					] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 | 
				
			||||||
@@ -14,6 +14,6 @@ import os
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.core.wsgi import get_wsgi_application
 | 
					from django.core.wsgi import get_wsgi_application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lostplaces.settings')
 | 
					os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_lostplaces.settings')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
application = get_wsgi_application()
 | 
					application = get_wsgi_application()
 | 
				
			||||||
@@ -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.models import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .forms import ExplorerCreationForm, ExplorerChangeForm
 | 
					from lostplaces.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)
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
from django.apps import AppConfig
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LostplacesAppConfig(AppConfig):
 | 
					class LostplacesAppConfig(AppConfig):
 | 
				
			||||||
    name = 'lostplaces_app'
 | 
					    name = 'lostplaces'
 | 
				
			||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
from django import forms
 | 
					from django import forms
 | 
				
			||||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
 | 
					from django.contrib.auth.forms import UserCreationForm, UserChangeForm
 | 
				
			||||||
from django.contrib.auth.models import User
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
from lostplaces_app.models import Place, PlaceImage, Voucher
 | 
					from lostplaces.models import Place, PlaceImage, Voucher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ExplorerCreationForm(UserCreationForm):
 | 
					class ExplorerCreationForm(UserCreationForm):
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
@@ -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,7 +20,6 @@ from taggit.managers import TaggableManager
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Create your models here.
 | 
					# Create your models here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class Explorer(models.Model):
 | 
					class Explorer(models.Model):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Profile that is linked to the a User.
 | 
					    Profile that is linked to the a User.
 | 
				
			||||||
@@ -33,7 +33,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):
 | 
				
			||||||
@@ -44,38 +44,25 @@ def create_user_profile(sender, instance, created, **kwargs):
 | 
				
			|||||||
def save_user_profile(sender, instance, **kwargs):
 | 
					def save_user_profile(sender, instance, **kwargs):
 | 
				
			||||||
    instance.explorer.save()
 | 
					    instance.explorer.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Taggable(models.Model):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    This abstract model represtens an object that is taggalble
 | 
				
			||||||
 | 
					    using django-taggit
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
class Voucher(models.Model):
 | 
					    tags = TaggableManager(blank=True)
 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Vouchers are authorization tokens to allow the registration of new users.
 | 
					 | 
				
			||||||
    A voucher has a code, a creation and a deletion date, which are all 
 | 
					 | 
				
			||||||
    positional. Creation date is being set automatically during voucher 
 | 
					 | 
				
			||||||
    creation. 
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    code = models.CharField(unique=True, max_length=30)
 | 
					class Mapable(models.Model):
 | 
				
			||||||
    created = models.DateTimeField(auto_now_add=True)
 | 
					    '''
 | 
				
			||||||
    expires = models.DateField()
 | 
					    This abstract model class represents an object that can be
 | 
				
			||||||
 | 
					    displayed on a map.
 | 
				
			||||||
    def __str__(self):
 | 
					    '''
 | 
				
			||||||
        return "Voucher " + str(self.pk)
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					 | 
				
			||||||
class Place (models.Model):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Place defines a lost place (location, name, description etc.).
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    name = models.CharField(max_length=50)
 | 
					    name = models.CharField(max_length=50)
 | 
				
			||||||
    submitted_when = models.DateTimeField(auto_now_add=True, null=True)
 | 
					 | 
				
			||||||
    submitted_by = models.ForeignKey(
 | 
					 | 
				
			||||||
        Explorer,
 | 
					 | 
				
			||||||
        on_delete=models.SET_NULL,
 | 
					 | 
				
			||||||
        null=True,
 | 
					 | 
				
			||||||
        blank=True,
 | 
					 | 
				
			||||||
        related_name='places'
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    location = models.CharField(max_length=50)
 | 
					 | 
				
			||||||
    latitude = models.FloatField(
 | 
					    latitude = models.FloatField(
 | 
				
			||||||
        validators=[
 | 
					        validators=[
 | 
				
			||||||
            MinValueValidator(-90),
 | 
					            MinValueValidator(-90),
 | 
				
			||||||
@@ -88,12 +75,55 @@ class Place (models.Model):
 | 
				
			|||||||
            MaxValueValidator(180)
 | 
					            MaxValueValidator(180)
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Submittable(models.Model):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    This abstract model class represents an object that can be submitted by
 | 
				
			||||||
 | 
					    an explorer.
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    submitted_when = models.DateTimeField(auto_now_add=True, null=True)
 | 
				
			||||||
 | 
					    submitted_by = models.ForeignKey(
 | 
				
			||||||
 | 
					        Explorer,
 | 
				
			||||||
 | 
					        on_delete=models.SET_NULL,
 | 
				
			||||||
 | 
					        null=True,
 | 
				
			||||||
 | 
					        blank=True,
 | 
				
			||||||
 | 
					        related_name='%(class)s'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Voucher(models.Model):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Vouchers are authorization tokens to allow the registration of new users.
 | 
				
			||||||
 | 
					    A voucher has a code, a creation and a deletion date, which are all 
 | 
				
			||||||
 | 
					    positional. Creation date is being set automatically during voucher 
 | 
				
			||||||
 | 
					    creation. 
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    code = models.CharField(unique=True, max_length=30)
 | 
				
			||||||
 | 
					    created_when = models.DateTimeField(auto_now_add=True)
 | 
				
			||||||
 | 
					    expires_when = models.DateTimeField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return "Voucher " + str(self.code)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Place(Submittable, Taggable, Mapable):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Place defines a lost place (location, name, description etc.).
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    location = models.CharField(max_length=50)
 | 
				
			||||||
    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 +133,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
 | 
				
			||||||
@@ -119,7 +149,7 @@ def generate_image_upload_path(instance, filename):
 | 
				
			|||||||
    return 'places/' + str(uuid.uuid4())+'.'+filename.split('.')[-1]
 | 
					    return 'places/' + str(uuid.uuid4())+'.'+filename.split('.')[-1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlaceImage (models.Model):
 | 
					class PlaceImage (Submittable):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    PlaceImage defines an image file object that points to a file in uploads/.
 | 
					    PlaceImage defines an image file object that points to a file in uploads/.
 | 
				
			||||||
    Intermediate image sizes are generated as defined in SIZES.
 | 
					    Intermediate image sizes are generated as defined in SIZES.
 | 
				
			||||||
@@ -131,15 +161,7 @@ class PlaceImage (models.Model):
 | 
				
			|||||||
    place = models.ForeignKey(
 | 
					    place = models.ForeignKey(
 | 
				
			||||||
        Place,
 | 
					        Place,
 | 
				
			||||||
        on_delete=models.CASCADE,
 | 
					        on_delete=models.CASCADE,
 | 
				
			||||||
        related_name='images'
 | 
					        related_name='placeimages'
 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    submitted_when = models.DateTimeField(auto_now_add=True, null=True)
 | 
					 | 
				
			||||||
    submitted_by = models.ForeignKey(
 | 
					 | 
				
			||||||
        Explorer,
 | 
					 | 
				
			||||||
        on_delete=models.SET_NULL,
 | 
					 | 
				
			||||||
        null=True,
 | 
					 | 
				
			||||||
        blank=True,
 | 
					 | 
				
			||||||
        related_name='images'
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 264 KiB  | 
| 
		 Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB  | 
| 
		 Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB  | 
| 
		 Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB  | 
| 
		 Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 816 B After Width: | Height: | Size: 816 B  | 
| 
		 Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B  | 
| 
		 Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 641 B  | 
| 
		 Before Width: | Height: | Size: 850 B After Width: | Height: | Size: 850 B  | 
| 
		 Before Width: | Height: | Size: 914 B After Width: | Height: | Size: 914 B  | 
| 
		 Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B  | 
| 
		 Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB  | 
| 
		 Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB  | 
| 
		 Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB  | 
| 
		 Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB  | 
| 
		 Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB  | 
| 
		 Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB  | 
| 
		 Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 717 B  | 
| 
		 Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB  | 
| 
		 Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB  | 
@@ -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=mapping_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">
 | 
				
			||||||
@@ -23,7 +23,7 @@
 | 
				
			|||||||
            <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">
 | 
					                <article class="LP-PlaceTeaser">
 | 
				
			||||||
                    <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.placeimages.first.filename.thumbnail.url}}" />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="LP-PlaceTeaser__Meta">
 | 
					                    <div class="LP-PlaceTeaser__Meta">
 | 
				
			||||||
                        <div class="LP-PlaceTeaser__Info">
 | 
					                        <div class="LP-PlaceTeaser__Info">
 | 
				
			||||||
@@ -34,7 +34,7 @@
 | 
				
			|||||||
            <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">
 | 
					                <article class="LP-PlaceTeaser">
 | 
				
			||||||
                    <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.placeimages.first.filename.thumbnail.url}}" />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                    <div class="LP-PlaceTeaser__Meta">
 | 
					                    <div class="LP-PlaceTeaser__Meta">
 | 
				
			||||||
                        <div class="LP-PlaceTeaser__Info">
 | 
					                        <div class="LP-PlaceTeaser__Info">
 | 
				
			||||||
@@ -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.all_points %}
 | 
				
			||||||
                        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 %}
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
@@ -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 %
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,9 +23,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <header class="LP-PlaceDetail__Header">
 | 
					    <header class="LP-PlaceDetail__Header">
 | 
				
			||||||
        <h1 class="LP-Headline">{{ place.name }}</h1>
 | 
					        <h1 class="LP-Headline">{{ place.name }}</h1>
 | 
				
			||||||
        {% if place.images.first.filename.hero.url %}
 | 
					        {% if place.placeimages.first.filename.hero.url %}
 | 
				
			||||||
        <figure class="LP-PlaceDetail__Image">
 | 
					        <figure class="LP-PlaceDetail__Image">
 | 
				
			||||||
            <img src="{{ place.images.first.filename.hero.url }}" class="LP-Image" />
 | 
					            <img src="{{ place.placeimages.first.filename.hero.url }}" class="LP-Image" />
 | 
				
			||||||
        </figure>
 | 
					        </figure>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
    </header>
 | 
					    </header>
 | 
				
			||||||
@@ -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=mapping_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>
 | 
				
			||||||
@@ -92,7 +92,7 @@
 | 
				
			|||||||
        <h1 class="LP-Headline">Bilder</h1>
 | 
					        <h1 class="LP-Headline">Bilder</h1>
 | 
				
			||||||
        <div class="LP-ImageGrid">
 | 
					        <div class="LP-ImageGrid">
 | 
				
			||||||
            <ul class="LP-ImageGrid__Container">
 | 
					            <ul class="LP-ImageGrid__Container">
 | 
				
			||||||
                {% for place_image in place.images.all %}
 | 
					                {% for place_image in place.placeimages.all %}
 | 
				
			||||||
                <li class="LP-ImageGrid__Item">
 | 
					                <li class="LP-ImageGrid__Item">
 | 
				
			||||||
                    <a href="{{ place_image.filename.large.url }}" class="LP-Link"><img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}"></a>
 | 
					                    <a href="{{ place_image.filename.large.url }}" class="LP-Link"><img class="LP-Image" src="{{ place_image.filename.thumbnail.url }}"></a>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
							
								
								
									
										59
									
								
								django_lostplaces/lostplaces/templates/place/place_list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					{% extends 'global.html'%}
 | 
				
			||||||
 | 
					{% load static %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block additional_head %}
 | 
				
			||||||
 | 
					<link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
 | 
				
			||||||
 | 
					<script src="{% static 'maps/ol.js' %}"></script>
 | 
				
			||||||
 | 
					{% endblock additional_head %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block title %}Lost Places{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block maincontent %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% include 'partials/osm_map.html' with config=mapping_config %}
 | 
				
			||||||
 | 
					<div class="LP-PlaceList">
 | 
				
			||||||
 | 
					    <h1 class="LP-Headline">Listing our places</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">
 | 
				
			||||||
 | 
					                <article class="LP-PlaceTeaser LP-PlaceTeaser--extended">
 | 
				
			||||||
 | 
					                    <div class="LP-PlaceTeaser__Image">
 | 
				
			||||||
 | 
					                        <img class="LP-Image" src="{{ place.placeimages.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 class="LP-PlaceTeaser__Description">
 | 
				
			||||||
 | 
					                            <p class="LP-Paragraph">
 | 
				
			||||||
 | 
					                                {% if place.description|length > 210 %}
 | 
				
			||||||
 | 
					                                {{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/flag.svg' %}" /></li>
 | 
				
			||||||
 | 
					                            </ul>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </article>
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					        </li>
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
 | 
					    </ul>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {% include 'partials/nav/pagination.html' %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock maincontent %}
 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 209 B  | 
@@ -6,7 +6,7 @@ from django.conf import settings
 | 
				
			|||||||
from django.template import Library, TemplateSyntaxError
 | 
					from django.template import Library, TemplateSyntaxError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#icons_json_path = getattr(settings, 'SVG_ICONS_SOURCE_FILE')
 | 
					#icons_json_path = getattr(settings, 'SVG_ICONS_SOURCE_FILE')
 | 
				
			||||||
icons_json_path = os.path.join(settings.BASE_DIR, 'lostplaces_app', 'static', 'icons', 'icons.icomoon.json')
 | 
					icons_json_path = os.path.join(settings.BASE_DIR, 'lostplaces', 'static', 'icons', 'icons.icomoon.json')
 | 
				
			||||||
icons_json = json.load(open(icons_json_path))
 | 
					icons_json = json.load(open(icons_json_path))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
register = Library()
 | 
					register = Library()
 | 
				
			||||||
							
								
								
									
										2
									
								
								django_lostplaces/lostplaces/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
							
								
								
									
										141
									
								
								django_lostplaces/lostplaces/tests/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,141 @@
 | 
				
			|||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					from django.core.exceptions import FieldDoesNotExist
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Creating a test user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ModelTestCase(TestCase):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    Base class for ModelTests.
 | 
				
			||||||
 | 
					    Parameters:
 | 
				
			||||||
 | 
					    - model : Class to test
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    model = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertField(self, field_name, field_class, must_have={}, must_not_have={}):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Tests if a field exists under the given name and
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            field = self.model._meta.get_field(field_name)
 | 
				
			||||||
 | 
					        except FieldDoesNotExist:
 | 
				
			||||||
 | 
					            self.fail(
 | 
				
			||||||
 | 
					                'Expecting %s to have a field named \'%s\'' % (
 | 
				
			||||||
 | 
					                    self.model.__name__,
 | 
				
			||||||
 | 
					                    field_name
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            type(field), field_class,
 | 
				
			||||||
 | 
					            msg='Expecting type of %s to be %s' % (
 | 
				
			||||||
 | 
					                str(field),
 | 
				
			||||||
 | 
					                field_class.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for key, value in must_have.items():
 | 
				
			||||||
 | 
					            if value:
 | 
				
			||||||
 | 
					                self.assertEqual(
 | 
				
			||||||
 | 
					                    getattr(field, key), value,
 | 
				
			||||||
 | 
					                    msg='Expeting the value of %s %s to be \'%s\'' % (
 | 
				
			||||||
 | 
					                        str(field),
 | 
				
			||||||
 | 
					                        key,
 | 
				
			||||||
 | 
					                        value
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.assertTrue(
 | 
				
			||||||
 | 
					                    hasattr(field, key),
 | 
				
			||||||
 | 
					                    msg='Expeting %s to have \'%s\'' % (
 | 
				
			||||||
 | 
					                        str(field),
 | 
				
			||||||
 | 
					                        key
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for key, value in must_not_have.items():
 | 
				
			||||||
 | 
					            if value:
 | 
				
			||||||
 | 
					                self.assertTrue(
 | 
				
			||||||
 | 
					                    getattr(field, key) != value,
 | 
				
			||||||
 | 
					                    msg='Expeting the value of %s %s to not be \'%s\'' % (
 | 
				
			||||||
 | 
					                        str(field),
 | 
				
			||||||
 | 
					                        key,
 | 
				
			||||||
 | 
					                        value
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.assertFalse(
 | 
				
			||||||
 | 
					                    hasattr(field, value),
 | 
				
			||||||
 | 
					                    msg='Expeting %s to not have \'%s\'' % (
 | 
				
			||||||
 | 
					                        str(field),
 | 
				
			||||||
 | 
					                        key
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return field
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertCharField(self, field_name, min_length, max_length, must_have={}, must_hot_have={}):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Tests if the given field is a char field and if its max_length
 | 
				
			||||||
 | 
					        is in min_length and max_legth
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        field = self.assertField(
 | 
				
			||||||
 | 
					            field_name, models.CharField, must_have, must_hot_have)
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            field.max_length in range(min_length, max_length),
 | 
				
			||||||
 | 
					            msg='Expeting %s  max_length to be in the range of %d and %d' % (
 | 
				
			||||||
 | 
					                str(field),
 | 
				
			||||||
 | 
					                min_length,
 | 
				
			||||||
 | 
					                max_length
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertFloatField(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,
 | 
				
			||||||
 | 
					        the validators of the field are also checked. The validator list of the field should
 | 
				
			||||||
 | 
					        look like 
 | 
				
			||||||
 | 
					        [MinValueValidator, MayValueValidator], if both values are passed,
 | 
				
			||||||
 | 
					        [MinValueValidator] if only min_value is passed,
 | 
				
			||||||
 | 
					        [MaxValueValidator] if only max_value is passed
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        field = self.assertField(
 | 
				
			||||||
 | 
					            field_name, models.FloatField, must_have, must_hot_have)
 | 
				
			||||||
 | 
					        if min_value:
 | 
				
			||||||
 | 
					            self.assertTrue(
 | 
				
			||||||
 | 
					                len(field.validators) >= 1,
 | 
				
			||||||
 | 
					                msg='Expecting the first valiator of %s to check the minimum' % (
 | 
				
			||||||
 | 
					                    str(field)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            self.assertEqual(
 | 
				
			||||||
 | 
					                field.validators[0].limit_value,
 | 
				
			||||||
 | 
					                min_value,
 | 
				
			||||||
 | 
					                msg='Expecting the min value of %s min to be at least %d' % (
 | 
				
			||||||
 | 
					                    str(field),
 | 
				
			||||||
 | 
					                    min_value
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        if max_value:
 | 
				
			||||||
 | 
					            index = 0
 | 
				
			||||||
 | 
					            if min_value:
 | 
				
			||||||
 | 
					                index += 1
 | 
				
			||||||
 | 
					            self.assertTrue(
 | 
				
			||||||
 | 
					                len(field.validators) >= index+1,
 | 
				
			||||||
 | 
					                msg='Expecting the second valiator of %s to check the maximum' % (
 | 
				
			||||||
 | 
					                    str(field)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            self.assertEqual(
 | 
				
			||||||
 | 
					                field.validators[1].limit_value,
 | 
				
			||||||
 | 
					                max_value,
 | 
				
			||||||
 | 
					                msg='Expecting the max value of %s min to be at most %d' % (
 | 
				
			||||||
 | 
					                    str(field),
 | 
				
			||||||
 | 
					                    max_value
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								django_lostplaces/lostplaces/tests/models/im_a_image.jpeg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 81 KiB  | 
@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.models import (
 | 
				
			||||||
 | 
					    Taggable,
 | 
				
			||||||
 | 
					    Mapable,
 | 
				
			||||||
 | 
					    Submittable
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from lostplaces.tests.models import ModelTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from taggit.managers import TaggableManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TaggableTestCase(ModelTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model = Taggable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_tags(self):
 | 
				
			||||||
 | 
					        self.assertField('tags', TaggableManager)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MapableTestCase(ModelTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model = Mapable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_name(self):
 | 
				
			||||||
 | 
					        self.assertCharField(
 | 
				
			||||||
 | 
					            field_name='name',
 | 
				
			||||||
 | 
					            min_length=10,
 | 
				
			||||||
 | 
					            max_length=100
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_latitude(self):
 | 
				
			||||||
 | 
					        self.assertFloatField(
 | 
				
			||||||
 | 
					            field_name='latitude',
 | 
				
			||||||
 | 
					            min_value=-90,
 | 
				
			||||||
 | 
					            max_value=90
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_longitude(self):
 | 
				
			||||||
 | 
					        self.assertFloatField(
 | 
				
			||||||
 | 
					            field_name='longitude',
 | 
				
			||||||
 | 
					            min_value=-180,
 | 
				
			||||||
 | 
					            max_value=180
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					class SubmittableTestCase(ModelTestCase):
 | 
				
			||||||
 | 
					    model = Submittable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_submitted_when(self):
 | 
				
			||||||
 | 
					        self.assertField(
 | 
				
			||||||
 | 
					            field_name='submitted_when',
 | 
				
			||||||
 | 
					            field_class=models.DateTimeField,
 | 
				
			||||||
 | 
					            must_have={'auto_now_add': True}
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_submitted_by(self):
 | 
				
			||||||
 | 
					        submitted_by = self.assertField(
 | 
				
			||||||
 | 
					            field_name='submitted_by',
 | 
				
			||||||
 | 
					            field_class=models.ForeignKey
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            submitted_by.remote_field.related_name,
 | 
				
			||||||
 | 
					            '%(class)s',
 | 
				
			||||||
 | 
					            msg='Expecting the related_name of %s to be \'%%(class)s\', got %s' % (
 | 
				
			||||||
 | 
					                str(submitted_by),
 | 
				
			||||||
 | 
					                submitted_by.remote_field.related_name
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            submitted_by.null,
 | 
				
			||||||
 | 
					            msg='Expecting %s to has null=True' % (
 | 
				
			||||||
 | 
					                str(submitted_by)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            submitted_by.blank,
 | 
				
			||||||
 | 
					            msg='Expecting %s to has blank=True' % (
 | 
				
			||||||
 | 
					                str(submitted_by)
 | 
				
			||||||
 | 
					            )    
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            submitted_by.remote_field.on_delete,
 | 
				
			||||||
 | 
					            models.SET_NULL,
 | 
				
			||||||
 | 
					            msg='Expecting %s to be null when reference is delete (models.SET_NULL)' % (
 | 
				
			||||||
 | 
					                str(submitted_by)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.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)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					from unittest import mock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.core.files import File
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.models import PlaceImage, Place
 | 
				
			||||||
 | 
					from lostplaces.tests.models import ModelTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from easy_thumbnails.fields import ThumbnailerImageField
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PlaceImageTestCase(ModelTestCase):
 | 
				
			||||||
 | 
					    model = PlaceImage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @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.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 setUp(self):
 | 
				
			||||||
 | 
					        self.place_image = PlaceImage.objects.get(id=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_description(self):
 | 
				
			||||||
 | 
					        self.assertField('description', models.TextField)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_filename(self):
 | 
				
			||||||
 | 
					        self.assertField('filename',ThumbnailerImageField)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_place(self):
 | 
				
			||||||
 | 
					        field = self.assertField('place', models.ForeignKey)
 | 
				
			||||||
 | 
					        self.assertEqual(field.remote_field.on_delete, models.CASCADE,
 | 
				
			||||||
 | 
					            msg='Expecting the deletion of %s to be cascading' % (
 | 
				
			||||||
 | 
					                str(field)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        expected_related_name = 'placeimages'
 | 
				
			||||||
 | 
					        self.assertEqual(field.remote_field.related_name, expected_related_name,
 | 
				
			||||||
 | 
					            msg='Expecting the related name of %s to be %s' % (
 | 
				
			||||||
 | 
					                str(field),
 | 
				
			||||||
 | 
					                expected_related_name
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_str(self):
 | 
				
			||||||
 | 
					        self.assertTrue(self.place_image.place.name.lower() in str(self.place_image).lower(),
 | 
				
			||||||
 | 
					            msg='Expecting %s.__str__ to contain  the name of the place' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def test_change_filename(self):
 | 
				
			||||||
 | 
					        path = self.place_image.filename.path
 | 
				
			||||||
 | 
					        self.place_image.filename = os.path.join(settings.MEDIA_ROOT, 'im_a_image_changed.jpeg')
 | 
				
			||||||
 | 
					        self.place_image.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.place_image.filename.path
 | 
				
			||||||
 | 
					        self.place_image.delete()
 | 
				
			||||||
 | 
					        self.assertFalse(
 | 
				
			||||||
 | 
					            os.path.isfile(path),
 | 
				
			||||||
 | 
					            msg='Expecting the file of an place_image to be deleteed when an place_image is deleted'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
							
								
								
									
										124
									
								
								django_lostplaces/lostplaces/tests/models/test_place_model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.models import Place
 | 
				
			||||||
 | 
					from lostplaces.tests.models import ModelTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PlaceTestCase(ModelTestCase):
 | 
				
			||||||
 | 
					    model = Place
 | 
				
			||||||
 | 
					    related_name = 'places'
 | 
				
			||||||
 | 
					    nullable = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @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):
 | 
				
			||||||
 | 
					        self.place = Place.objects.get(id=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_location(self):
 | 
				
			||||||
 | 
					        self.assertCharField(
 | 
				
			||||||
 | 
					            field_name='location',
 | 
				
			||||||
 | 
					            min_length=10,
 | 
				
			||||||
 | 
					            max_length=100
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_decsription(self):
 | 
				
			||||||
 | 
					        self.assertField('description', models.TextField)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_average_latlon(self):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Tests the average latitude/longitude calculation of a list
 | 
				
			||||||
 | 
					        of 10 places
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        place_list = []
 | 
				
			||||||
 | 
					        for i in range(10):
 | 
				
			||||||
 | 
					            place = Place.objects.get(id=1)
 | 
				
			||||||
 | 
					            place.id = None
 | 
				
			||||||
 | 
					            place.latitude = i+1
 | 
				
			||||||
 | 
					            place.longitude = i+10
 | 
				
			||||||
 | 
					            place.save()
 | 
				
			||||||
 | 
					            place_list.append(place)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        avg_latlon = Place.average_latlon(place_list)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        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' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(avg_latlon['longitude'], 14.5,
 | 
				
			||||||
 | 
					            msg='%s: average longitude missmatch' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_average_latlon_one_place(self):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Tests the average latitude/longitude calculation of a list
 | 
				
			||||||
 | 
					        of one place
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        place = Place.objects.get(id=1)
 | 
				
			||||||
 | 
					        avg_latlon = Place.average_latlon([place])
 | 
				
			||||||
 | 
					        self.assertEqual(avg_latlon['latitude'], place.latitude,
 | 
				
			||||||
 | 
					            msg='%s:(one place) average latitude missmatch' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(avg_latlon['longitude'], place.longitude,
 | 
				
			||||||
 | 
					            msg='%s: (one place) average longitude missmatch' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_average_latlon_no_places(self):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Tests the average latitude/longitude calculation of 
 | 
				
			||||||
 | 
					        an empty list
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        avg_latlon = Place.average_latlon([])
 | 
				
			||||||
 | 
					        self.assertEqual(avg_latlon['latitude'], 0,
 | 
				
			||||||
 | 
					            msg='%s: (no places) average latitude missmatch' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(avg_latlon['longitude'], 0,
 | 
				
			||||||
 | 
					            msg='%s: a(no places) verage longitude missmatch' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_str(self):
 | 
				
			||||||
 | 
					        place = self.place
 | 
				
			||||||
 | 
					        self.assertTrue(place.name.lower() in str(place).lower(),
 | 
				
			||||||
 | 
					            msg='Expecting %s.__str__ to contain the name' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.models import Voucher
 | 
				
			||||||
 | 
					from lostplaces.tests.models import ModelTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VoucheTestCase(ModelTestCase):
 | 
				
			||||||
 | 
					    model = Voucher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        Voucher.objects.create(
 | 
				
			||||||
 | 
					            code='ayDraJCCwfhcFiYmSR5GrcjcchDfcahv',
 | 
				
			||||||
 | 
					            expires_when=timezone.now() + datetime.timedelta(days=1)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        self.voucher = Voucher.objects.get(id=1)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def test_voucher_code(self):
 | 
				
			||||||
 | 
					        self.assertCharField(
 | 
				
			||||||
 | 
					            field_name='code',
 | 
				
			||||||
 | 
					            min_length=10,
 | 
				
			||||||
 | 
					            max_length=100,
 | 
				
			||||||
 | 
					            must_have={'unique': True}
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def test_voucher_created(self):
 | 
				
			||||||
 | 
					        self.assertField(
 | 
				
			||||||
 | 
					            field_name='created_when',
 | 
				
			||||||
 | 
					            field_class=models.DateTimeField,
 | 
				
			||||||
 | 
					            must_have={'auto_now_add': True}
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def test_voucher_expires(self):
 | 
				
			||||||
 | 
					        self.assertField(
 | 
				
			||||||
 | 
					            field_name='expires_when',
 | 
				
			||||||
 | 
					            field_class=models.DateTimeField,
 | 
				
			||||||
 | 
					            must_not_have={'auto_now_add': True}
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_str(self):
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            self.voucher.code.lower() in str(self.voucher).lower(),
 | 
				
			||||||
 | 
					            msg='Expecting %s.__str__ to contain the voucher code' % (
 | 
				
			||||||
 | 
					                self.model.__name__
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
							
								
								
									
										224
									
								
								django_lostplaces/lostplaces/tests/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.models import Taggable, Mapable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from taggit.models import Tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ViewTestCase(TestCase):
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    This is a mixni for testing views. It provides functionality to
 | 
				
			||||||
 | 
					    test the context, forms and HTTP Response of responses. 
 | 
				
			||||||
 | 
					    All methods take responses, so this base class can be used
 | 
				
			||||||
 | 
					    with django's RequestFactory and Test-Client
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    view = None
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def assertContext(self, response, key, value=None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Checks weather the response's context has the given key
 | 
				
			||||||
 | 
					        and, if passed, checks the value
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            key in response.context,
 | 
				
			||||||
 | 
					            msg='Expecting the context of %s to have an attribute \'%s\'' % (
 | 
				
			||||||
 | 
					                self.view.__name__,
 | 
				
			||||||
 | 
					                key
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if value:
 | 
				
			||||||
 | 
					            self.assertEqual(
 | 
				
			||||||
 | 
					                value,
 | 
				
			||||||
 | 
					                response.context[key],
 | 
				
			||||||
 | 
					                msg='Expecting the context of %s to have %s set to \'%s\'' % (
 | 
				
			||||||
 | 
					                    self.view.__name__,
 | 
				
			||||||
 | 
					                    key, 
 | 
				
			||||||
 | 
					                    str(value)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def assertHasForm(self, response, key, form_class):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Checks if response has a form under the given key and if 
 | 
				
			||||||
 | 
					        the forms class matches.
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.assertContext(response, key)
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            type(response.context[key]),
 | 
				
			||||||
 | 
					            form_class,
 | 
				
			||||||
 | 
					            msg='Expecting %s\'s context.%s to be of the type %s' % (
 | 
				
			||||||
 | 
					                self.view.__name__,
 | 
				
			||||||
 | 
					                key,
 | 
				
			||||||
 | 
					                form_class.__name__
 | 
				
			||||||
 | 
					            ) 
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertHttpCode(self, response, code):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Checks if the response has the given status code
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            response.status_code, code,
 | 
				
			||||||
 | 
					            msg='Expecting an HTTP %s response, but got HTTP %s' % (
 | 
				
			||||||
 | 
					                code,
 | 
				
			||||||
 | 
					                response.status_code
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def assertHttpRedirect(self, response, redirect_to=None):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        Checks weather the response redirected, and if passed, 
 | 
				
			||||||
 | 
					        if it redirected to the expected loaction
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            300 <= response.status_code < 400,
 | 
				
			||||||
 | 
					            'Expected an HTTP 3XX (redirect) response, but got HTTP %s' %
 | 
				
			||||||
 | 
					            response.status_code
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'location' in response,
 | 
				
			||||||
 | 
					            msg='Expecting a redirect to have an location, got none'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if redirect_to:
 | 
				
			||||||
 | 
					            self.assertEqual(
 | 
				
			||||||
 | 
					                response['location'],
 | 
				
			||||||
 | 
					                redirect_to,
 | 
				
			||||||
 | 
					                msg='Expecing the response to redirect to %s, where redirected to %s instea' % (
 | 
				
			||||||
 | 
					                    str(redirect_to),
 | 
				
			||||||
 | 
					                    str(response['location'])
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertHttpOK(self, response):
 | 
				
			||||||
 | 
					        self.assertHttpCode(response, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertHttpCreated(self, response):
 | 
				
			||||||
 | 
					        self.assertHttpCode(response, 201)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					class TaggableViewTestCaseMixin:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertTaggableContext(self, context):
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'all_tags' in context,
 | 
				
			||||||
 | 
					            msg='Expecting the context for taggable to contain an \'all_tags\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for tag in context['all_tags']:
 | 
				
			||||||
 | 
					            self.assertTrue(
 | 
				
			||||||
 | 
					                isinstance(tag, Tag),
 | 
				
			||||||
 | 
					                msg='Expecting all entries to be an instance of %s, got %s' % (
 | 
				
			||||||
 | 
					                    str(Tag),
 | 
				
			||||||
 | 
					                    str(type(tag))
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'submit_form' in context,
 | 
				
			||||||
 | 
					            msg='Expecting the context for taggable to contain \'submit_form\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'tagged_item' in context,
 | 
				
			||||||
 | 
					            msg='Expecting the context for taggable to contain \'tagged_item\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            isinstance(context['tagged_item'], Taggable),
 | 
				
			||||||
 | 
					            msg='Expecting the tagged_item to be an instance of %s' % (
 | 
				
			||||||
 | 
					                str(Taggable)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'submit_url_name' in context,
 | 
				
			||||||
 | 
					            msg='Expecting the context for taggable to contain \'submit_url_name\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            type(context['submit_url_name']) == str,
 | 
				
			||||||
 | 
					            msg='Expecting submit_url_name to be of type string'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'delete_url_name' in context,
 | 
				
			||||||
 | 
					            msg='Expecting the context for taggable to contain \'delete_url_name\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            type(context['delete_url_name']) == str,
 | 
				
			||||||
 | 
					            msg='Expecting delete_url_name to be of type string'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					class MapableViewTestCaseMixin:
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def assertMapableContext(self, context):
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'all_points' in context,
 | 
				
			||||||
 | 
					            msg='Expecting the context for mapable point to contain \'all_points\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for point in context['all_points']:
 | 
				
			||||||
 | 
					            self.assertTrue(
 | 
				
			||||||
 | 
					                isinstance(point, Mapable),
 | 
				
			||||||
 | 
					                msg='Expecting all entries to be an instance of %s, got %s' % (
 | 
				
			||||||
 | 
					                    str(Mapable),
 | 
				
			||||||
 | 
					                    str(type(point))
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'map_center' in context,
 | 
				
			||||||
 | 
					            msg='Expecting the context for mapable point to contain \'map_center\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'latitude' in context['map_center'],
 | 
				
			||||||
 | 
					            msg='Expecting the map center to contain an \'latitude\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            isinstance(context['map_center']['latitude'], float) or isinstance(context['map_center']['latitude'], int),
 | 
				
			||||||
 | 
					            msg='Expecting the latitude of the map center to be numeric, type %s given' % (
 | 
				
			||||||
 | 
					                str(type(context['map_center']['latitude']))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            -90 <= context['map_center']['latitude'] <= 90,
 | 
				
			||||||
 | 
					            msg='Expecting the latitude of map center to be in the range of -90 and 90'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'longitude' in context['map_center'],
 | 
				
			||||||
 | 
					            msg='Expecting the map center to contain an \'longitude\' attribute'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            isinstance(context['map_center']['longitude'], float) or isinstance(context['map_center']['longitude'], int),
 | 
				
			||||||
 | 
					            msg='Expecting the longitude of the map center to be numeric, type %s given' % (
 | 
				
			||||||
 | 
					                str(type(context['map_center']['longitude']))
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            -180 <= context['map_center']['longitude'] <= 180,
 | 
				
			||||||
 | 
					            msg='Expecting the longitude of map center to be in the range of -180 and 180'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
							
								
								
									
										87
									
								
								django_lostplaces/lostplaces/tests/views/test_base_views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,87 @@
 | 
				
			|||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.test import TestCase, RequestFactory, Client
 | 
				
			||||||
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User, AnonymousUser
 | 
				
			||||||
 | 
					from django.contrib.messages.storage.fallback import FallbackStorage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.models import Place
 | 
				
			||||||
 | 
					from lostplaces.views import IsAuthenticatedMixin
 | 
				
			||||||
 | 
					from lostplaces.tests.views import ViewTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestIsAuthenticated(ViewTestCase):
 | 
				
			||||||
 | 
					    view = IsAuthenticatedMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def setUpTestData(cls):
 | 
				
			||||||
 | 
					        user = User.objects.create_user(
 | 
				
			||||||
 | 
					            username='testpeter',
 | 
				
			||||||
 | 
					            password='Develop123'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        self.client = Client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_logged_in(self):
 | 
				
			||||||
 | 
					        request = RequestFactory().get('/')
 | 
				
			||||||
 | 
					        request.user = User.objects.get(id=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = IsAuthenticatedMixin.as_view()(request)
 | 
				
			||||||
 | 
					        # Expecting a 405 because IsAuthenticatedMixin has no 'get' method
 | 
				
			||||||
 | 
					        self.assertHttpMethodNotAllowed(response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_not_logged_in(self):
 | 
				
			||||||
 | 
					        request = RequestFactory().get('/someurl1234')
 | 
				
			||||||
 | 
					        request.user = AnonymousUser()
 | 
				
			||||||
 | 
					        request.session = 'session'
 | 
				
			||||||
 | 
					        messages = FallbackStorage(request)
 | 
				
			||||||
 | 
					        request._messages = messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = IsAuthenticatedMixin.as_view()(request)
 | 
				
			||||||
 | 
					        self.assertHttpRedirect(response, '?'.join([str(reverse_lazy('login')), 'next=/someurl1234']))
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        response = self.client.get(response['Location']) 
 | 
				
			||||||
 | 
					        self.assertTrue(len(messages) > 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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):
 | 
				
			||||||
 | 
					        self.client = Client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        self. client = Client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_submitter(self):
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(reverse_lazy('place_edit', kwargs={'pk': 1}))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_is_no_submitter(self):
 | 
				
			||||||
 | 
					        User.objects.create_user(
 | 
				
			||||||
 | 
					            username='manfred',
 | 
				
			||||||
 | 
					            password='Develop123'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.client.login(username='manfred', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(reverse_lazy('place_edit', kwargs={'pk': 1}))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 403)
 | 
				
			||||||
 | 
					        self.assertTrue(response.context['messages'])
 | 
				
			||||||
 | 
					        self.assertTrue(len(response.context['messages']) > 0)
 | 
				
			||||||
							
								
								
									
										126
									
								
								django_lostplaces/lostplaces/tests/views/test_place_views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,126 @@
 | 
				
			|||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.test import TestCase, Client
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from lostplaces.models import Place
 | 
				
			||||||
 | 
					from lostplaces.views import (
 | 
				
			||||||
 | 
					    PlaceCreateView,
 | 
				
			||||||
 | 
					    PlaceListView,
 | 
				
			||||||
 | 
					    PlaceDetailView
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from lostplaces.forms import PlaceImageCreateForm, PlaceForm
 | 
				
			||||||
 | 
					from lostplaces.tests.views import (
 | 
				
			||||||
 | 
					    ViewTestCase,
 | 
				
			||||||
 | 
					    TaggableViewTestCaseMixin,
 | 
				
			||||||
 | 
					    MapableViewTestCaseMixin
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestPlaceCreateView(ViewTestCase):
 | 
				
			||||||
 | 
					    view = PlaceCreateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @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):
 | 
				
			||||||
 | 
					        self.client = Client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_has_forms(self):
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(reverse('place_create'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertHasForm(response, 'place_image_form', PlaceImageCreateForm)
 | 
				
			||||||
 | 
					        self.assertHasForm(response, 'place_form', PlaceForm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestPlaceListView(ViewTestCase):
 | 
				
			||||||
 | 
					    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 setUp(self):
 | 
				
			||||||
 | 
					        self.client = Client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_list_view(self):
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(reverse('place_list'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertContext(response, 'mapping_config')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixin, ViewTestCase):
 | 
				
			||||||
 | 
					    view = PlaceDetailView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @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_context(self):
 | 
				
			||||||
 | 
					        self.client.login(username='testpeter', password='Develop123')
 | 
				
			||||||
 | 
					        response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'tagging_config' in response.context,
 | 
				
			||||||
 | 
					            msg='Expecting the context of %s to have an \'tagging_config\'' % (
 | 
				
			||||||
 | 
					                str(self.view)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertTaggableContext(response.context['tagging_config'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertTrue(
 | 
				
			||||||
 | 
					            'mapping_config' in response.context,
 | 
				
			||||||
 | 
					            msg='Expecting the context of %s to have an \'mapping_config\'' % (
 | 
				
			||||||
 | 
					                str(self.view)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertMapableContext(response.context['mapping_config'])
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
from django.urls import path
 | 
					from django.urls import path
 | 
				
			||||||
from .views import (
 | 
					from lostplaces.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')
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
							
								
								
									
										3
									
								
								django_lostplaces/lostplaces/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from lostplaces.views.base_views import *
 | 
				
			||||||
 | 
					from lostplaces.views.views import * 
 | 
				
			||||||
 | 
					from lostplaces.views.place_views import *
 | 
				
			||||||
@@ -9,9 +9,9 @@ from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			|||||||
from django.shortcuts import redirect
 | 
					from django.shortcuts import redirect
 | 
				
			||||||
from django.urls import reverse_lazy
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces_app.models import Place
 | 
					from lostplaces.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 = ''
 | 
				
			||||||
@@ -9,13 +9,13 @@ from django.contrib.messages.views import SuccessMessageMixin
 | 
				
			|||||||
from django.shortcuts import render, redirect
 | 
					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.models import Place, PlaceImage
 | 
				
			||||||
from lostplaces_app.views import IsAuthenticated, IsPlaceSubmitter
 | 
					from lostplaces.views import IsAuthenticatedMixin, IsPlaceSubmitterMixin
 | 
				
			||||||
from lostplaces_app.forms import PlaceForm, PlaceImageCreateForm, TagSubmitForm
 | 
					from lostplaces.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['mapping_config'] = {
 | 
				
			||||||
 | 
					            'all_points': 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 ],
 | 
					            'mapping_config': {
 | 
				
			||||||
            'place_map_center': [ place.latitude, place.longitude ],
 | 
					                'all_points': [ 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()
 | 
				
			||||||
@@ -85,23 +90,19 @@ class PlaceCreateView(IsAuthenticated, View):
 | 
				
			|||||||
                    submitter=submitter
 | 
					                    submitter=submitter
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            kwargs_to_pass = {
 | 
					 | 
				
			||||||
                'pk': place.pk
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            messages.success(
 | 
					            messages.success(
 | 
				
			||||||
                self.request, 'Successfully created place.')
 | 
					                self.request,
 | 
				
			||||||
            return redirect(reverse_lazy('place_detail', kwargs=kwargs_to_pass))
 | 
					                'Successfully created place.'
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            return redirect(reverse_lazy('place_detail', kwargs={'pk': place.pk}))
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            context = {
 | 
					 | 
				
			||||||
                'form': form_place
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Usually the browser should have checked the form before sending.
 | 
					            # Usually the browser should have checked the form before sending.
 | 
				
			||||||
            messages.error(
 | 
					            messages.error(
 | 
				
			||||||
                self.request, 'Please fill in all required fields.')
 | 
					                self.request,
 | 
				
			||||||
            return render(request, 'place/place_create.html', context)
 | 
					                'Please fill in all required fields.'
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            return render(request, 'place/place_create.html', context={'form': form_place})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _apply_multipart_image_upload(self, files, place, submitter):
 | 
					    def _apply_multipart_image_upload(self, files, place, submitter):
 | 
				
			||||||
        for image in files:
 | 
					        for image in files:
 | 
				
			||||||
@@ -112,7 +113,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.'
 | 
				
			||||||
@@ -7,11 +7,11 @@ from django.urls import reverse_lazy
 | 
				
			|||||||
from django.shortcuts import render, redirect, get_object_or_404
 | 
					from django.shortcuts import render, redirect, get_object_or_404
 | 
				
			||||||
from django.http import HttpResponseForbidden
 | 
					from django.http import HttpResponseForbidden
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces_app.forms import ExplorerCreationForm, TagSubmitForm
 | 
					from lostplaces.forms import ExplorerCreationForm, TagSubmitForm
 | 
				
			||||||
from lostplaces_app.models import Place, PhotoAlbum
 | 
					from lostplaces.models import Place, PhotoAlbum
 | 
				
			||||||
from lostplaces_app.views.base_views import IsAuthenticated
 | 
					from lostplaces.views.base_views import IsAuthenticatedMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from lostplaces_app.views.base_views import (
 | 
					from lostplaces.views.base_views import (
 | 
				
			||||||
    PlaceAssetCreateView, 
 | 
					    PlaceAssetCreateView, 
 | 
				
			||||||
    PlaceAssetDeleteView,
 | 
					    PlaceAssetDeleteView,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -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,
 | 
					            'all_points': place_list,
 | 
				
			||||||
            'place_map_center': place_map_center
 | 
					            'mapping_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)
 | 
				
			||||||
@@ -7,7 +7,7 @@ import os
 | 
				
			|||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lostplaces.settings')
 | 
					    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_lostplaces.settings')
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        from django.core.management import execute_from_command_line
 | 
					        from django.core.management import execute_from_command_line
 | 
				
			||||||
    except ImportError as exc:
 | 
					    except ImportError as exc:
 | 
				
			||||||
@@ -1,59 +0,0 @@
 | 
				
			|||||||
{% extends 'global.html'%}
 | 
					 | 
				
			||||||
{% load static %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block additional_head %}
 | 
					 | 
				
			||||||
    <link rel="stylesheet" href="{% static 'maps/ol.css' %}" type="text/css">
 | 
					 | 
				
			||||||
    <script src="{% static 'maps/ol.js' %}"></script>
 | 
					 | 
				
			||||||
{% endblock additional_head %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block title %}Lost Places{% endblock %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block maincontent %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% include 'partials/osm_map.html' %}
 | 
					 | 
				
			||||||
<div class="LP-PlaceList">
 | 
					 | 
				
			||||||
    <h1 class="LP-Headline">Listing our places</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">
 | 
					 | 
				
			||||||
                    <article class="LP-PlaceTeaser LP-PlaceTeaser--extended">
 | 
					 | 
				
			||||||
                        <div class="LP-PlaceTeaser__Image">
 | 
					 | 
				
			||||||
                            <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 class="LP-PlaceTeaser__Description">
 | 
					 | 
				
			||||||
                                <p class="LP-Paragraph">
 | 
					 | 
				
			||||||
                                    {% if place.description|length > 210 %}
 | 
					 | 
				
			||||||
                                        {{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/flag.svg' %}" /></li>
 | 
					 | 
				
			||||||
                                </ul>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    </article>
 | 
					 | 
				
			||||||
                </a>
 | 
					 | 
				
			||||||
            </li>
 | 
					 | 
				
			||||||
        {% endfor %}
 | 
					 | 
				
			||||||
    </ul>
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    {% include 'partials/nav/pagination.html' %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
{% endblock maincontent %}
 | 
					 | 
				
			||||||
@@ -1,13 +0,0 @@
 | 
				
			|||||||
from django.db import models as django_models
 | 
					 | 
				
			||||||
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]
 | 
					 | 
				
			||||||
@@ -1,121 +0,0 @@
 | 
				
			|||||||
from django.db import models
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TestModel:
 | 
					 | 
				
			||||||
    '''
 | 
					 | 
				
			||||||
    Base class for Lostplaces models
 | 
					 | 
				
			||||||
    '''
 | 
					 | 
				
			||||||
    model_name = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _test_field(self, field_name, field_class):
 | 
					 | 
				
			||||||
        '''
 | 
					 | 
				
			||||||
        Tests if a field exists under the given name and
 | 
					 | 
				
			||||||
        if the field is of the right type
 | 
					 | 
				
			||||||
        '''
 | 
					 | 
				
			||||||
        field = self.object._meta.get_field(field_name)
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            field,
 | 
					 | 
				
			||||||
            msg="%s has no field named '%s'" % (
 | 
					 | 
				
			||||||
                self.model_name,
 | 
					 | 
				
			||||||
                field_name
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertEqual(
 | 
					 | 
				
			||||||
            type(field), field_class, 
 | 
					 | 
				
			||||||
            msg='%s.%s name field is no CharField' % (
 | 
					 | 
				
			||||||
                self.model_name,
 | 
					 | 
				
			||||||
                field_name
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        return field
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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
 | 
					 | 
				
			||||||
        is in min_length and max_legth
 | 
					 | 
				
			||||||
        '''
 | 
					 | 
				
			||||||
        field = self._test_field(field_name, models.CharField)
 | 
					 | 
				
			||||||
        self.assertEqual(
 | 
					 | 
				
			||||||
            type(field), models.CharField, 
 | 
					 | 
				
			||||||
            msg='%s.%s name field is no CharField' % (
 | 
					 | 
				
			||||||
                self.model_name,
 | 
					 | 
				
			||||||
                field_name
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					 | 
				
			||||||
            field.max_length in range(min_length, max_length),
 | 
					 | 
				
			||||||
            msg='%s.%s field max_length not in range of %d and %d' % (
 | 
					 | 
				
			||||||
                self.model_name,
 | 
					 | 
				
			||||||
                field_name,
 | 
					 | 
				
			||||||
                min_length,
 | 
					 | 
				
			||||||
                max_length
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    def _test_float_field(self, field_name, min_value=None, max_value=None):
 | 
					 | 
				
			||||||
        '''
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
        look like 
 | 
					 | 
				
			||||||
        [MinValueValidator, MayValueValidator], if both values are passed,
 | 
					 | 
				
			||||||
        [MinValueValidator] if only min_value is passed,
 | 
					 | 
				
			||||||
        [MaxValueValidator] if only max_value is passed
 | 
					 | 
				
			||||||
        '''
 | 
					 | 
				
			||||||
        field = self._test_field(field_name, models.FloatField)
 | 
					 | 
				
			||||||
        if min_value:
 | 
					 | 
				
			||||||
            self.assertTrue(
 | 
					 | 
				
			||||||
                len(field.validators) >= 1,
 | 
					 | 
				
			||||||
                msg='%s.%s first validator should check minimum' % (
 | 
					 | 
				
			||||||
                    self.model_name,
 | 
					 | 
				
			||||||
                    field_name
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            self.assertEqual(
 | 
					 | 
				
			||||||
                field.validators[0].limit_value,
 | 
					 | 
				
			||||||
                min_value,
 | 
					 | 
				
			||||||
                msg='%s.%s min value missmatch' % (
 | 
					 | 
				
			||||||
                    self.model_name,
 | 
					 | 
				
			||||||
                    field_name
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        if max_value:
 | 
					 | 
				
			||||||
            index = 0
 | 
					 | 
				
			||||||
            if min_value:
 | 
					 | 
				
			||||||
                index += 1
 | 
					 | 
				
			||||||
            self.assertTrue(
 | 
					 | 
				
			||||||
                len(field.validators) >= index+1,
 | 
					 | 
				
			||||||
                msg='%s.%s second validator should check maximum' % (
 | 
					 | 
				
			||||||
                    self.model_name,
 | 
					 | 
				
			||||||
                    field_name
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            self.assertEqual(
 | 
					 | 
				
			||||||
                field.validators[1].limit_value,
 | 
					 | 
				
			||||||
                max_value,
 | 
					 | 
				
			||||||
                msg='%s.%s max value missmatch' % (
 | 
					 | 
				
			||||||
                    self.model_name,
 | 
					 | 
				
			||||||
                    field_name
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TestSubmittable(TestModel):
 | 
					 | 
				
			||||||
    model_name='<Class>'
 | 
					 | 
				
			||||||
    related_name = None
 | 
					 | 
				
			||||||
    nullable = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_submitted_when(self):
 | 
					 | 
				
			||||||
        submitted_when = self._test_field('submitted_when', models.DateTimeField)
 | 
					 | 
				
			||||||
        self.assertTrue(submitted_when.auto_now_add, 
 | 
					 | 
				
			||||||
            msg='%s.submitted_when should be auto_now_add' % (
 | 
					 | 
				
			||||||
                self.model_name
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_submitted_by(self):
 | 
					 | 
				
			||||||
        submitted_by = self._test_field('submitted_by',models.ForeignKey)
 | 
					 | 
				
			||||||
        if self.related_name:
 | 
					 | 
				
			||||||
            self.assertEqual(submitted_by.remote_field.related_name, self.related_name)
 | 
					 | 
				
			||||||
        if self.nullable:
 | 
					 | 
				
			||||||
            self.assertTrue(submitted_by.null,)
 | 
					 | 
				
			||||||
            self.assertTrue(submitted_by.blank)
 | 
					 | 
				
			||||||
            self.assertEqual(submitted_by.remote_field.on_delete, models.SET_NULL)
 | 
					 | 
				
			||||||
@@ -1,55 +0,0 @@
 | 
				
			|||||||
import datetime
 | 
					 | 
				
			||||||
from unittest import mock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.test import TestCase
 | 
					 | 
				
			||||||
from django.db import models
 | 
					 | 
				
			||||||
from django.core.files import File
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from lostplaces_app.models import PlaceImage
 | 
					 | 
				
			||||||
from lostplaces_app.tests.models import TestSubmittable
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def mock_place_image():
 | 
					 | 
				
			||||||
    return 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):
 | 
					 | 
				
			||||||
    model_name = 'PlaceImage'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def setUp(self):
 | 
					 | 
				
			||||||
        self.object = mock_place_image()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_description(self):
 | 
					 | 
				
			||||||
        self._test_field('description', models.TextField)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_filename(self):
 | 
					 | 
				
			||||||
        self._test_field('filename',ThumbnailerImageField)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_place(self):
 | 
					 | 
				
			||||||
        field = self._test_field('place', models.ForeignKey)
 | 
					 | 
				
			||||||
        self.assertEqual(field.remote_field.on_delete, models.CASCADE,
 | 
					 | 
				
			||||||
            msg='%s.%s deleting of %s should be cascadinf' % (
 | 
					 | 
				
			||||||
                self.model_name,
 | 
					 | 
				
			||||||
                'place',
 | 
					 | 
				
			||||||
                self.model_name
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertEqual(field.remote_field.related_name, 'images',
 | 
					 | 
				
			||||||
            msg='%s.%s related name should be images' % (
 | 
					 | 
				
			||||||
                self.model_name, 
 | 
					 | 
				
			||||||
                'place'                
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_str(self):
 | 
					 | 
				
			||||||
        place_image = mock_place_image()
 | 
					 | 
				
			||||||
        self.assertEqual(str(place_image), ' '.join([place_image.place.name, str(place_image.pk)]))
 | 
					 | 
				
			||||||