From e18e8f0108cf99aeddda6429a318be7e54f8b25d Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Sun, 21 Feb 2016 00:15:20 +0100 Subject: [PATCH 01/36] Initial work to rework the user map. --- requirements.txt | 10 +- setup.py | 2 +- user_map/admin.py | 47 ++- user_map/app_settings.py | 107 +++-- user_map/auth_backend.py | 2 +- user_map/forms/user.py | 323 ++++++++------- user_map/migrations/0001_initial.py | 44 +- user_map/migrations/0002_populate_roles.py | 6 +- user_map/models/role.py | 9 +- user_map/models/user.py | 176 +++----- user_map/models/user_manager.py | 220 +++++----- user_map/serializers.py | 38 ++ user_map/static/user_map/js/user-map.js | 43 +- user_map/templates/user_map/add_user.html | 38 ++ user_map/templates/user_map/base.html | 28 +- user_map/templates/user_map/base_page.html | 74 ++-- user_map/templates/user_map/index.html | 89 +--- .../user_map/user_info_popup_content.html | 22 +- .../templates/user_map/user_menu_button.html | 46 +-- user_map/tests/model_factories.py | 2 +- user_map/tests/test_models.py | 2 +- user_map/tests/test_views.py | 2 +- user_map/urls.py | 54 +-- user_map/views.py | 379 ++++-------------- 24 files changed, 761 insertions(+), 1002 deletions(-) create mode 100644 user_map/serializers.py create mode 100644 user_map/templates/user_map/add_user.html diff --git a/requirements.txt b/requirements.txt index b274f95..07ad158 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -Django==1.7 -django-leaflet==0.14.1 -psycopg2==2.5.4 -factory-boy==2.4.1 -django-bootstrap-form==3.1 +Django==1.8.9 +django-leaflet==0.18.0 +psycopg2==2.6.1 +factory-boy==2.6.0 +django-bootstrap-form==3.2 diff --git a/setup.py b/setup.py index cf16a59..ffee6d4 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ install_requires=[ "Django==1.7", "django-leaflet==0.14.1", - "psycopg2==2.5.4", + "psycopg2==2.5.4, "factory-boy==2.4.1", "django-bootstrap-form==3.1", ], diff --git a/user_map/admin.py b/user_map/admin.py index 2bf49c3..f7c86e0 100644 --- a/user_map/admin.py +++ b/user_map/admin.py @@ -1,28 +1,39 @@ -# coding=utf-8 + # coding=utf-8 """Model Admin Class.""" from django.contrib.gis import admin +from django.contrib.auth import get_user_model +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from leaflet.admin import LeafletGeoAdmin -from user_map.models import User, Role +from user_map.models import UserMap, Role -class UserAdmin(LeafletGeoAdmin): - """Admin Class for User Model.""" - list_display = ('name', 'email', 'role', 'website', 'email_updates', - 'last_login', 'is_confirmed', 'is_admin') - list_filter = ['role', 'is_confirmed', 'is_admin'] - search_fields = ['name', 'email'] - fieldsets = [ - ('Basic Information', { - 'fields': [ - 'name', 'email', 'website', 'role', 'email_updates']}), - ('Location', {'fields': ['location']}), - ('Advanced Information', { - 'fields': ['is_confirmed', 'is_active', 'is_admin', 'last_login'] - }), - ] +class UserMapAdmin(LeafletGeoAdmin): + """Admin Class for User Map Model.""" + model = UserMap + can_delete = False + # list_display = ('name', 'email', 'role', 'website', 'email_updates', + # 'last_login', 'is_confirmed', 'is_admin') + # list_filter = ['role', 'is_confirmed', 'is_admin'] + # search_fields = ['name', 'email'] + # fieldsets = [ + # ('Basic Information', { + # 'fields': [ + # 'name', 'email', 'website', 'role', 'email_updates']}), + # ('Location', {'fields': ['location']}), + # ('Advanced Information', { + # 'fields': ['is_confirmed', 'is_active', 'is_admin', 'last_login'] + # }), + # ] +from django.contrib.auth.models import User -admin.site.register(User, UserAdmin) +# class UserAdmin(BaseUserAdmin): +# """Define the new User Admin plugging the UserMap.""" +# inlines = (UserMapAdmin, ) + +# Re-register User Admin +# admin.site.unregister(get_user_model()) +admin.site.register(UserMap, UserMapAdmin) admin.site.register(Role) diff --git a/user_map/app_settings.py b/user_map/app_settings.py index f3a86e3..3906180 100644 --- a/user_map/app_settings.py +++ b/user_map/app_settings.py @@ -10,45 +10,78 @@ """ from django.conf import settings + +# USER_MODEL: The auth user model set in project's settings +USER_MODEL = settings.AUTH_USER_MODEL + +# Leaflet Settings +LEAFLET_CONFIG = { + 'TILES': [ + ( + 'OpenStreetMap', # The title + 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', # The tile URL + ('© OpenStreetMap and contributors, under an ' + 'open license') # The attribution + )] +} +LEAFLET_TILES = LEAFLET_CONFIG['TILES'][0] + +# USER MAP Settings +default_setting = { + 'project_name': 'InaSAFE', + 'brand_logo': '', + 'favicon_file': 'user_map/img/user-icon.png', + 'login_url': 'login', + 'marker': { + 'icon': 'user_map/img/user-icon.png', + 'shadow': 'user_map/img/shadow-icon.png' # or 'shadow': None + }, + 'roles': [ + { + 'name': 'User', + 'badge': 'user_map/img/inasafe-badge-user' + }, + { + 'name': 'Trainer', + 'badge': 'user_map/img/inasafe-badge-trainer' + }, + { + 'name': 'Developer', + 'badge': 'user_map/img/inasafe-badge-developer' + } + ], + 'api_user_fields': [ + 'username', 'first_name', 'last_name' + ], +} + +user_map_settings = getattr(settings, 'USER_MAP', default_setting) + # PROJECT_NAME: The project name for this apps e.g InaSAFE -default_project_name = 'InaSAFE' -PROJECT_NAME = getattr(settings, 'USER_MAP_PROJECT_NAME', default_project_name) +PROJECT_NAME = user_map_settings.get( + 'project_name', default_setting['project_name']) # LOGO/BRAND -default_brand_logo = 'user_map/img/logo.png' -BRAND_LOGO = getattr(settings, 'USER_MAP_BRAND_LOGO', default_brand_logo) +BRAND_LOGO = user_map_settings.get('brand_logo', default_setting['brand_logo']) # FAVICON_FILE: Favicon for this apps -default_favicon_file = 'user_map/img/user-icon.png' -FAVICON_FILE = getattr(settings, 'USER_MAP_FAVICON_FILE', default_favicon_file) - -# USER ROLES: All user roles and their icons -default_user_roles = [ - dict( - name='User', - icon='user_map/img/user-icon.png', - shadow_icon='user_map/img/shadow-icon.png'), - dict( - name='Trainer', - icon='user_map/img/trainer-icon.png', - shadow_icon='user_map/img/shadow-icon.png'), - dict( - name='Developer', - icon='user_map/img/developer-icon.png', - shadow_icon='user_map/img/shadow-icon.png')] -USER_ROLES = getattr(settings, 'USER_MAP_USER_ROLES', default_user_roles) - -# MAIL SENDER -default_mail_sender = 'noreply@inasafe.org' -DEFAULT_FROM_MAIL = getattr(settings, 'DEFAULT_FROM_MAIL', default_mail_sender) - -# LEAFLET CONFIG -default_leaflet_tiles = ( - 'OpenStreetMap', - 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', - ('© OpenStreetMap' - ' and contributors, under an open ' - 'license') -) -LEAFLET_TILES = getattr(settings, 'LEAFLET_TILES', default_leaflet_tiles) +FAVICON_FILE = user_map_settings.get( + 'favicon_file', default_setting['favicon_file']) + + +# LOGIN_VIEW: The view to the login page +LOGIN_VIEW = user_map_settings.get( + 'login_url', default_setting['login_url']) + +# MARKER +MARKER = user_map_settings.get( + 'marker', default_setting['marker']) + +# ROLES: All user roles and their badges +ROLES = user_map_settings.get('roles', default_setting['roles']) + +# API_USER_FIELDS +API_USER_FIELDS = user_map_settings.get( + 'api_user_fields', default_setting['api_user_fields']) diff --git a/user_map/auth_backend.py b/user_map/auth_backend.py index a21dfba..87cdccc 100644 --- a/user_map/auth_backend.py +++ b/user_map/auth_backend.py @@ -1,6 +1,6 @@ # coding=utf-8 """Authentication backend class for InaSAFE User Map.""" -from user_map.models.user import User +from user.models.user import User class UserMapAuthBackend(object): diff --git a/user_map/forms/user.py b/user_map/forms/user.py index 3b24006..7b0c6ba 100644 --- a/user_map/forms/user.py +++ b/user_map/forms/user.py @@ -3,165 +3,176 @@ from django.contrib.gis import forms from django.contrib.auth.forms import PasswordResetForm from leaflet.forms.widgets import LeafletWidget +from leaflet.forms.fields import PointField -from user_map.models import User, Role +from user_map.models import UserMap, Role -class RegistrationForm(forms.ModelForm): - """Form for user model.""" - name = forms.CharField( - required=True, - label='Your name', - widget=forms.TextInput( - attrs={'placeholder': 'John Doe'}) - ) - email = forms.EmailField( - required=True, - label='Your email', - widget=forms.EmailInput( - attrs={ - 'placeholder': 'john@doe.com'}) - ) - password = forms.CharField( - required=True, - label='Your password', - widget=forms.PasswordInput() - ) - password2 = forms.CharField( - required=True, - label='Your password (again)', - widget=forms.PasswordInput() - ) - website = forms.URLField( - required=False, - label='Your website', - widget=forms.URLInput( - attrs={'placeholder': 'http://john.doe.com'}) - ) - location = forms.PointField( - label='Click your location on the map', - widget=LeafletWidget()) - role = forms.ModelChoiceField( - label='Your role', - queryset=Role.objects.filter(sort_number__gte=1), - initial=1) - email_updates = forms.BooleanField( - required=False, - label='Receive project news and updates') - - class Meta: - """Association between models and this form.""" - model = User - fields = ['name', 'email', 'password', 'password2', 'website', 'role', - 'location', 'email_updates'] - - def clean(self): - """Verifies that the values entered into the password fields match.""" - cleaned_data = super(RegistrationForm, self).clean() - if 'password' in cleaned_data and 'password2' in cleaned_data: - if cleaned_data['password'] != cleaned_data['password2']: - raise forms.ValidationError( - "Passwords don't match. Please enter both fields again.") - return cleaned_data - - def save(self, commit=True): - """Save form. - - :param commit: Whether committed to db or not. - :type commit: bool - """ - user = super(RegistrationForm, self).save(commit=False) - user.set_password(self.cleaned_data['password']) - if commit: - user.save() - return user - - -class LoginForm(forms.Form): - """Form for user to log in.""" - class Meta: - """Meta of the form.""" - fields = ['email', 'password'] - - email = forms.EmailField( - widget=forms.EmailInput( - attrs={ - 'class': 'form-control', - 'placeholder': 'john@doe.com', - }) - ) - password = forms.CharField( - widget=forms.PasswordInput( - attrs={ - 'class': 'form-control', - 'placeholder': 'Your s3cr3T password' - }) - ) - - -class BasicInformationForm(forms.ModelForm): - """Form for Basic Information model.""" - name = forms.CharField( - required=True, - label='Your name', - widget=forms.TextInput( - attrs={ - 'placeholder': 'John Doe'}) - ) - email = forms.EmailField( - required=True, - label='Your email', - widget=forms.EmailInput( - attrs={ - 'readonly': 'readonly', - 'placeholder': 'john@doe.com'}) - ) - website = forms.URLField( - required=False, - label='Your website', - widget=forms.URLInput( - attrs={ - 'placeholder': 'http://john.doe.com'}) - ) - role = forms.ModelChoiceField( - label='Your role', - queryset=Role.objects.filter(sort_number__gte=1), - initial=1) - email_updates = forms.BooleanField( - required=False, - label='Receive project news and updates') - location = forms.PointField( - label='Click your location on the map', - widget=LeafletWidget()) - - class Meta: - """Association between models and this form.""" - model = User - fields = ['name', 'email', 'website', 'role', 'location', - 'email_updates'] - - def save(self, commit=True): - """Save form. - - :param commit: Whether committed to db or not. - :type commit: bool - """ - user = super(BasicInformationForm, self).save(commit=False) - if commit: - user.save() - return user - - -class CustomPasswordResetForm(PasswordResetForm): - """Form for password reset containing email input.""" - email = forms.EmailField( - required=True, - label='Email', - widget=forms.EmailInput( - attrs={ - 'placeholder': 'john@doe.com'}) - ) +class UserMapForm(forms.ModelForm): + """Form for user model.""" class Meta: """Association between models and this form.""" - model = User + model = UserMap + exclude = ['user'] + + location = PointField() + +# class RegistrationForm(forms.ModelForm): +# """Form for user model.""" +# name = forms.CharField( +# required=True, +# label='Your name', +# widget=forms.TextInput( +# attrs={'placeholder': 'John Doe'}) +# ) +# email = forms.EmailField( +# required=True, +# label='Your email', +# widget=forms.EmailInput( +# attrs={ +# 'placeholder': 'john@doe.com'}) +# ) +# password = forms.CharField( +# required=True, +# label='Your password', +# widget=forms.PasswordInput() +# ) +# password2 = forms.CharField( +# required=True, +# label='Your password (again)', +# widget=forms.PasswordInput() +# ) +# website = forms.URLField( +# required=False, +# label='Your website', +# widget=forms.URLInput( +# attrs={'placeholder': 'http://john.doe.com'}) +# ) +# location = forms.PointField( +# label='Click your location on the map', +# widget=LeafletWidget()) +# role = forms.ModelChoiceField( +# label='Your role', +# queryset=Role.objects.filter(sort_number__gte=1), +# initial=1) +# email_updates = forms.BooleanField( +# required=False, +# label='Receive project news and updates') +# +# class Meta: +# """Association between models and this form.""" +# model = UserMap +# fields = ['name', 'email', 'password', 'password2', 'website', 'role', +# 'location', 'email_updates'] +# +# def clean(self): +# """Verifies that the values entered into the password fields match.""" +# cleaned_data = super(RegistrationForm, self).clean() +# if 'password' in cleaned_data and 'password2' in cleaned_data: +# if cleaned_data['password'] != cleaned_data['password2']: +# raise forms.ValidationError( +# "Passwords don't match. Please enter both fields again.") +# return cleaned_data +# +# def save(self, commit=True): +# """Save form. +# +# :param commit: Whether committed to db or not. +# :type commit: bool +# """ +# user = super(RegistrationForm, self).save(commit=False) +# user.set_password(self.cleaned_data['password']) +# if commit: +# user.save() +# return user +# +# +# class LoginForm(forms.Form): +# """Form for user to log in.""" +# class Meta: +# """Meta of the form.""" +# fields = ['email', 'password'] +# +# email = forms.EmailField( +# widget=forms.EmailInput( +# attrs={ +# 'class': 'form-control', +# 'placeholder': 'john@doe.com', +# }) +# ) +# password = forms.CharField( +# widget=forms.PasswordInput( +# attrs={ +# 'class': 'form-control', +# 'placeholder': 'Your s3cr3T password' +# }) +# ) +# +# +# class BasicInformationForm(forms.ModelForm): +# """Form for Basic Information model.""" +# name = forms.CharField( +# required=True, +# label='Your name', +# widget=forms.TextInput( +# attrs={ +# 'placeholder': 'John Doe'}) +# ) +# email = forms.EmailField( +# required=True, +# label='Your email', +# widget=forms.EmailInput( +# attrs={ +# 'readonly': 'readonly', +# 'placeholder': 'john@doe.com'}) +# ) +# website = forms.URLField( +# required=False, +# label='Your website', +# widget=forms.URLInput( +# attrs={ +# 'placeholder': 'http://john.doe.com'}) +# ) +# role = forms.ModelChoiceField( +# label='Your role', +# queryset=Role.objects.filter(sort_number__gte=1), +# initial=1) +# email_updates = forms.BooleanField( +# required=False, +# label='Receive project news and updates') +# location = forms.PointField( +# label='Click your location on the map', +# widget=LeafletWidget()) +# +# class Meta: +# """Association between models and this form.""" +# model = UserMap +# fields = ['name', 'email', 'website', 'role', 'location', +# 'email_updates'] +# +# def save(self, commit=True): +# """Save form. +# +# :param commit: Whether committed to db or not. +# :type commit: bool +# """ +# user = super(BasicInformationForm, self).save(commit=False) +# if commit: +# user.save() +# return user +# +# +# class CustomPasswordResetForm(PasswordResetForm): +# """Form for password reset containing email input.""" +# email = forms.EmailField( +# required=True, +# label='Email', +# widget=forms.EmailInput( +# attrs={ +# 'placeholder': 'john@doe.com'}) +# ) +# +# class Meta: +# """Association between models and this form.""" +# model = UserMap diff --git a/user_map/migrations/0001_initial.py b/user_map/migrations/0001_initial.py index 3ca5ff6..77ac181 100644 --- a/user_map/migrations/0001_initial.py +++ b/user_map/migrations/0001_initial.py @@ -1,53 +1,35 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations -import django.utils.timezone +from django.db import migrations, models +from django.conf import settings +import user_map.models.user import django.contrib.gis.db.models.fields class Migration(migrations.Migration): dependencies = [ + ('auth', '0006_require_contenttypes_0002'), ] operations = [ migrations.CreateModel( - name='User', + name='Role', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login')), - ('name', models.CharField(help_text=b'Your name.', max_length=100, verbose_name=b'Name')), - ('email', models.EmailField(help_text=b'Your email.', unique=True, max_length=75, verbose_name=b'E-mail')), - ('website', models.URLField(help_text=b'Optional link to your personal or organisation web site.', verbose_name=b'Website', blank=True)), - ('location', django.contrib.gis.db.models.fields.PointField(help_text=b'Where are you?', srid=4326, max_length=255, verbose_name=b'Location')), - ('email_updates', models.BooleanField(default=False, help_text=b'Tick this to receive occasional news email messages.', verbose_name=b'Receiving Updates')), - ('date_joined', models.DateTimeField(auto_now_add=True, verbose_name=b'Join Date')), - ('is_active', models.BooleanField(default=True, help_text=b'Whether this user is still active or not (a user could be banned or deleted).', verbose_name=b'Active Status')), - ('is_admin', models.BooleanField(default=False, help_text=b'Whether this user is admin or not.', verbose_name=b'Admin Status')), - ('is_confirmed', models.BooleanField(default=False, help_text=b'Whether this user has approved their entry by email.', verbose_name=b'Confirmation Status')), - ('key', models.CharField(help_text=b'Confirmation key for user to activate their account.', max_length=40, verbose_name=b'Confirmation Key')), + ('name', models.CharField(help_text=b'How would you define your participation?', unique=True, max_length=100)), + ('badge', models.CharField(help_text=b'The path to the badge', max_length=100)), ], - options={ - }, - bases=(models.Model,), ), migrations.CreateModel( - name='Role', + name='UserMap', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(help_text=b'How would you define your participation?', unique=True, max_length=100)), - ('sort_number', models.IntegerField(help_text=b'Sorting order for role in role list.', null=True, blank=True)), + ('user', models.OneToOneField(primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), + ('location', django.contrib.gis.db.models.fields.PointField(help_text=b'Where are you?', srid=4326, max_length=255, verbose_name=b'Location')), + ('image', models.ImageField(validators=[user_map.models.user.validate_image], upload_to=user_map.models.user.image_path, blank=True, help_text=b'Your photo', verbose_name=b'Image')), + ('is_hidden', models.BooleanField(default=False, help_text=b'Do you wish to hide yourself on the map?', verbose_name=b'Hidden Status')), + ('roles', models.ManyToManyField(to='user_map.Role', verbose_name=b'OSM Roles')), ], - options={ - }, - bases=(models.Model,), - ), - migrations.AddField( - model_name='user', - name='role', - field=models.ForeignKey(verbose_name=b'Role', to='user_map.Role'), - preserve_default=True, ), ] diff --git a/user_map/migrations/0002_populate_roles.py b/user_map/migrations/0002_populate_roles.py index 838fb98..a86d7a6 100644 --- a/user_map/migrations/0002_populate_roles.py +++ b/user_map/migrations/0002_populate_roles.py @@ -3,7 +3,7 @@ from django.db import models, migrations -from user_map.app_settings import USER_ROLES +from user_map.app_settings import ROLES def populate_roles(apps, schema_editor): @@ -16,8 +16,8 @@ def populate_roles(apps, schema_editor): :type schema_editor: django.db.backends.schema """ Role = apps.get_model('user_map', 'Role') - for idx, user_role in enumerate(USER_ROLES): - Role.objects.create(name=user_role['name'], sort_number=(idx + 1)) + for idx, user_role in enumerate(ROLES): + Role.objects.create(name=user_role['name'], badge=user_role['badge']) class Migration(migrations.Migration): diff --git a/user_map/models/role.py b/user_map/models/role.py index 64095db..8ac8a5a 100644 --- a/user_map/models/role.py +++ b/user_map/models/role.py @@ -15,10 +15,11 @@ class Meta: null=False, blank=False, unique=True) - sort_number = models.IntegerField( - help_text='Sorting order for role in role list.', - null=True, - blank=True) + badge = models.CharField( + help_text='The path to the badge', + max_length=100, + null=False, + blank=False) def __unicode__(self): return self.name diff --git a/user_map/models/user.py b/user_map/models/user.py index 1f3cb8a..a699691 100644 --- a/user_map/models/user.py +++ b/user_map/models/user.py @@ -1,38 +1,55 @@ # coding=utf-8 """Model class of custom user for InaSAFE User Map.""" -from django.contrib.auth.models import AbstractBaseUser +import os +import uuid + from django.contrib.gis.db import models -from django.utils.crypto import get_random_string +from django.core.exceptions import ValidationError -from user_map.models.user_manager import CustomUserManager from user_map.models.role import Role - from user_map.utilities.utilities import wrap_number +from user_map.app_settings import USER_MODEL + + +def validate_image(fieldfile_obj): + """Validate the image uploaded by user. + + :param fieldfile_obj: The object of the file field. + :type fieldfile_obj: File (django.core.files) + """ + file_size = fieldfile_obj.file.size + size_limit_mb = 2.0 + size_limit = size_limit_mb * 1024 * 1024 + if file_size > size_limit: + raise ValidationError( + 'Maximum image size is %s MB' % size_limit_mb) + + +def image_path(instance, file_name): + """Return the proper image path to upload. + + :param file_name: The original file name. + :type file_name: str + """ + _, ext = os.path.splitext(file_name) + + file_name = '%s%s' % (uuid.uuid4().hex, ext) + return os.path.join( + 'user_map', + 'images', + file_name) -class User(AbstractBaseUser): - """User class for InaSAFE User Map.""" +class UserMap(models.Model): + """User Map class as a OneToOne to AUTH_USER_MODEL.""" class Meta: """Meta class.""" app_label = 'user_map' - name = models.CharField( - verbose_name='Name', - help_text='Your name.', - max_length=100, - null=False, - blank=False) - email = models.EmailField( - verbose_name='E-mail', - help_text='Your email.', - null=False, - blank=False, - unique=True) - website = models.URLField( - verbose_name='Website', - help_text='Optional link to your personal or organisation web site.', - null=False, - blank=True) + user = models.OneToOneField( + USER_MODEL, + on_delete=models.CASCADE, + primary_key=True) location = models.PointField( verbose_name='Location', help_text='Where are you?', @@ -40,102 +57,33 @@ class Meta: max_length=255, null=False, blank=False) - role = models.ForeignKey(Role, verbose_name='Role', blank=False) - email_updates = models.BooleanField( - verbose_name='Receiving Updates', - help_text='Tick this to receive occasional news email messages.', - default=False) - date_joined = models.DateTimeField( - verbose_name='Join Date', - auto_now_add=True) - is_active = models.BooleanField( - verbose_name='Active Status', - help_text='Whether this user is still active or not (a user could be ' - 'banned or deleted).', - default=True) - is_admin = models.BooleanField( - verbose_name='Admin Status', - help_text='Whether this user is admin or not.', - default=False) - is_confirmed = models.BooleanField( - verbose_name='Confirmation Status', - help_text='Whether this user has approved their entry by email.', + roles = models.ManyToManyField( + Role, + verbose_name='Roles', + blank=False) + image = models.ImageField( + verbose_name='Image', + help_text='Your photo', + upload_to=image_path, null=False, + blank=True, + validators=[validate_image]) + is_hidden = models.BooleanField( + verbose_name='Hidden Status', + help_text='Do you wish to hide yourself on the map?', default=False) - key = models.CharField( - verbose_name='Confirmation Key', - help_text='Confirmation key for user to activate their account.', - max_length=40) - - objects = CustomUserManager() - - USERNAME_FIELD = 'email' - REQUIRED_FIELDS = ['name'] - - def __unicode__(self): - return self.name - - def get_full_name(self): - """ A longer formal identifier of the user. - - :return: The full name of a user. - :rtype: str - """ - return self.name - - def get_short_name(self): - """ A shorter formal identifier of the user. - - :return: The full name of a user. - :rtype: str - """ - return self.name - - @property - def is_staff(self): - """The staff status of a user. - - Staff is determined by the admin status of a user. - - :return: True if the user is an admin, otherwise False. - :rtype: bool - """ - return self.is_admin - - # noinspection PyUnusedLocal - def has_perm(self, perm, obj=None): - """Returns true if the user has the named permission. - - :param perm: The permission. - :type perm: str - - :param obj: The object that will be used to check the permission. - :type obj: object - - :return: The permission status. - :rtype: bool - """ - return self.is_admin - - # noinspection PyUnusedLocal - def has_module_perms(self, app_label): - """Returns True if the user has permission to access models of the app. - - :param app_label: The application. - :type app_label: str - - :return: The permission status. - :rtype: bool - """ - return self.is_admin def save(self, *args, **kwargs): - """Override save method.""" - if not self.pk: - # New object here - self.key = get_random_string() + # """Override save method.""" + # if self.pk: + # # Saving a not new object + # user = UserMap.objects.get(pk=self.pk) + # # Remove the old image if it's new image + # if user.image != self.image: + # user.image.delete(save=False) + # Wrap location data self.location.x = wrap_number(self.location.x, [-180, 180]) self.location.y = wrap_number(self.location.y, [-90, 90]) - super(User, self).save() + super(UserMap, self).save() diff --git a/user_map/models/user_manager.py b/user_map/models/user_manager.py index 110becd..9f5c4b1 100644 --- a/user_map/models/user_manager.py +++ b/user_map/models/user_manager.py @@ -1,110 +1,110 @@ -# coding=utf-8 -"""Custom User Manager for user of InaSAFE User Map.""" -from django.contrib.gis.db.models import GeoManager -from django.contrib.auth.models import BaseUserManager -from django.contrib.gis.geos import Point -from django.utils.crypto import get_random_string - -from user_map.models.role import Role - - -class CustomUserManager(BaseUserManager, GeoManager): - """Custom user manager for user map.""" - class Meta: - """Meta class.""" - app_label = 'user_map' - - def create_user( - self, - name, - email, - location, - role, - email_updates, - website='', - password=None): - """Create and save a User. - - :param name: The name of the user. - :type name: str - - :param email: The email of the user. - :type email: str - - :param location: The location of the user in (long, lat) - :type location: Point - - :param role: The role of the user. - :type role: Role - - :param email_updates: The status email_updates of the user. - :type email_updates: bool - - :param website: The website of the user. - :type website: str - - :param password: The password of the user. - :type password: str - """ - if not name: - raise ValueError('User must have name.') - - if not email: - raise ValueError('User must have an email address.') - - if not location: - raise ValueError('User must have location.') - - if not role: - raise ValueError('User must have role.') - - if not email_updates: - raise ValueError('User must have email_updates status.') - - user = self.model( - name=name, - email=self.normalize_email(email), - location=location, - role=role, - email_updates=email_updates, - website=website, - key=get_random_string() - ) - - user.set_password(password) - user.save(using=self._db) - return user - - def create_superuser(self, name, email, password): - """Create and save a superuser. - - :param name: The name of the superuser. - :type name: str - - :param email: The email of the superuser. - :type email: str - - :param password: The password of the superuser. - :type password: str - """ - # Use predefined location, role, email_updates, is_active, is_admin - location = Point(106.8, -6.2) - try: - role = Role.objects.get(name='Super User') - except Role.DoesNotExist: - role = Role(name='Super User', sort_number=-999) - role.save() - - user = self.create_user( - name, - email, - location=location, - role=role, - email_updates=True, - password=password) - user.email_updates = True - user.is_confirmed = True - user.is_active = True - user.is_admin = True - user.save(using=self._db) - return user +# # coding=utf-8 +# """Custom User Manager for user of InaSAFE User Map.""" +# from django.contrib.gis.db.models import GeoManager +# from django.contrib.auth.models import BaseUserManager +# from django.contrib.gis.geos import Point +# from django.utils.crypto import get_random_string +# +# from user_map.models.role import Role +# +# +# class CustomUserManager(BaseUserManager, GeoManager): +# """Custom user manager for user map.""" +# class Meta: +# """Meta class.""" +# app_label = 'user_map' +# +# def create_user( +# self, +# name, +# email, +# location, +# role, +# email_updates, +# website='', +# password=None): +# """Create and save a User. +# +# :param name: The name of the user. +# :type name: str +# +# :param email: The email of the user. +# :type email: str +# +# :param location: The location of the user in (long, lat) +# :type location: Point +# +# :param role: The role of the user. +# :type role: Role +# +# :param email_updates: The status email_updates of the user. +# :type email_updates: bool +# +# :param website: The website of the user. +# :type website: str +# +# :param password: The password of the user. +# :type password: str +# """ +# if not name: +# raise ValueError('User must have name.') +# +# if not email: +# raise ValueError('User must have an email address.') +# +# if not location: +# raise ValueError('User must have location.') +# +# if not role: +# raise ValueError('User must have role.') +# +# if not email_updates: +# raise ValueError('User must have email_updates status.') +# +# user = self.model( +# name=name, +# email=self.normalize_email(email), +# location=location, +# role=role, +# email_updates=email_updates, +# website=website, +# key=get_random_string() +# ) +# +# user.set_password(password) +# user.save(using=self._db) +# return user +# +# def create_superuser(self, name, email, password): +# """Create and save a superuser. +# +# :param name: The name of the superuser. +# :type name: str +# +# :param email: The email of the superuser. +# :type email: str +# +# :param password: The password of the superuser. +# :type password: str +# """ +# # Use predefined location, role, email_updates, is_active, is_admin +# location = Point(106.8, -6.2) +# try: +# role = Role.objects.get(name='Super User') +# except Role.DoesNotExist: +# role = Role(name='Super User', sort_number=-999) +# role.save() +# +# user = self.create_user( +# name, +# email, +# location=location, +# role=role, +# email_updates=True, +# password=password) +# user.email_updates = True +# user.is_confirmed = True +# user.is_active = True +# user.is_admin = True +# user.save(using=self._db) +# return user diff --git a/user_map/serializers.py b/user_map/serializers.py new file mode 100644 index 0000000..7dfe079 --- /dev/null +++ b/user_map/serializers.py @@ -0,0 +1,38 @@ +# coding=utf-8 +"""Serializers for User Map.""" +from django.template import loader +from django.contrib.auth import get_user_model + + +from rest_framework_gis.serializers import GeoFeatureModelSerializer +from rest_framework.serializers import ModelSerializer +from rest_framework import serializers + +from user_map.models import UserMap +from user_map.app_settings import API_USER_FIELDS + + +class UserSerializer(ModelSerializer): + """User Serializer.""" + class Meta: + model = get_user_model() + fields = tuple(field for field in API_USER_FIELDS) + + +class UserMapSerializer(GeoFeatureModelSerializer): + """User Map Serializer.""" + user = UserSerializer(read_only=True) + popup_content = serializers.SerializerMethodField() + + class Meta: + model = UserMap + id_field = False + geo_field = 'location' + fields = ('user', 'roles', 'image', 'popup_content') + + def get_popup_content(self, obj): + user_detail = [getattr(obj.user, field) for field in API_USER_FIELDS] + content = loader.render_to_string( + 'user_map/user_info_popup_content.html', + {'user': obj, 'user_detail': user_detail}) + return content diff --git a/user_map/static/user_map/js/user-map.js b/user_map/static/user_map/js/user-map.js index 1d67b34..7820c24 100644 --- a/user_map/static/user_map/js/user-map.js +++ b/user_map/static/user_map/js/user-map.js @@ -18,31 +18,38 @@ * @property popupContent Property of properties. * @function bindPopup Bind popup to marker */ -function addUsers(url, role) { +function addUsers(url, users_layer, icon) { $.ajax({ type: 'GET', url: url, dataType: 'json', - data: { - user_role: role['name'] - }, success: function (response) { - L.geoJson( - response.users, - { - onEachFeature: onEachFeature, - pointToLayer: function (feature, latlng) { - return L.marker(latlng,{icon: role['icon'] }); - } - }).addTo(role['layer']); + //L.geoJson( + // response, + // { + // onEachFeature: onEachFeature, + // pointToLayer: function (feature, latlng) { + // //return L.marker(latlng,{icon: role['icon'] }); + // return L.marker(latlng); + // } + // }).addTo(users_layer); + L.geoJson(response, { + onEachFeature: function (feature, layer) { + layer.bindPopup(feature.properties.popup_content); + }, + pointToLayer: function(feature, latlng) { + return L.marker(latlng, {icon: icon}) + } + }).addTo(users_layer) } }); - function onEachFeature(feature, layer) { - // Set the popup content if it does have the content - if (feature.properties && feature.properties.popupContent) { - layer.bindPopup(feature.properties.popupContent); - } - } + //function onEachFeature(feature, layer) { + // // Set the popup content if it does have the content + // if (feature.properties && feature.properties.popupContent) { + // //layer.bindPopup(feature.properties.popupContent); + // layer.bindPopup(feature.properties.username); + // } + //} } /** diff --git a/user_map/templates/user_map/add_user.html b/user_map/templates/user_map/add_user.html new file mode 100644 index 0000000..ef428f6 --- /dev/null +++ b/user_map/templates/user_map/add_user.html @@ -0,0 +1,38 @@ +{% extends "user_map/base_page.html" %} +{% load leaflet_tags %} +{% load bootstrap %} + +{% block head_resources %} + {{ block.super }} + {% leaflet_js plugins="forms" %} + {% leaflet_css plugins="forms" %} +{% endblock head_resources %} + +{% block main_content %} +
+ +
+ {% if messages %} + {% for message in messages %} + {% if 'success' in message.tags %} +
+ x + {{ message }} +
+ {% endif %} + {% endfor %} + {% endif %} +
+ {% csrf_token %} + {{ form|bootstrap_horizontal:'col-lg-3'}} +
+
+ +
+
+
+
+
+{% endblock main_content %} diff --git a/user_map/templates/user_map/base.html b/user_map/templates/user_map/base.html index d90e684..a55819b 100644 --- a/user_map/templates/user_map/base.html +++ b/user_map/templates/user_map/base.html @@ -1,4 +1,5 @@ {% load staticfiles %} +{% load leaflet_tags %} @@ -8,6 +9,9 @@ {% block title %} {{ PROJECT_NAME }} User Map{% endblock %} {% block head_resources %} + + {% leaflet_js %} + {% leaflet_css %} @@ -16,22 +20,24 @@ + {% endblock head_resources %} - -
- {% block navbar %} - {% endblock navbar %} -
+ +
+ {% block navbar %} + {% endblock navbar %} +
+ + {% block main_content %} + {% endblock main_content %} - {% block main_content %} - {% endblock main_content %} + {% block footer %} + {% endblock footer %} - {% block footer %} - {% endblock footer %} + {% block js_container %} + {% endblock js_container %} - {% block js_container %} - {% endblock js_container %} diff --git a/user_map/templates/user_map/base_page.html b/user_map/templates/user_map/base_page.html index 90b444c..d2f02cd 100644 --- a/user_map/templates/user_map/base_page.html +++ b/user_map/templates/user_map/base_page.html @@ -2,43 +2,43 @@ {% load staticfiles %} {% block navbar %} - +{# #} {% endblock navbar %} {% block main_content %} diff --git a/user_map/templates/user_map/index.html b/user_map/templates/user_map/index.html index 31da34e..755bdd6 100644 --- a/user_map/templates/user_map/index.html +++ b/user_map/templates/user_map/index.html @@ -3,46 +3,12 @@ {% load leaflet_tags %} {% block navbar %} - {% endblock navbar%} {% block head_resources %} {{ block.super }} - {% leaflet_js %} - {% leaflet_css %} +{# {% leaflet_js %}#} +{# {% leaflet_css %}#} #} -{# #} -{# #} -{% endblock navbar %} - {% block main_content %} {% endblock main_content %} diff --git a/user_map/templates/user_map/index.html b/user_map/templates/user_map/index.html index 755bdd6..a4e0ef1 100644 --- a/user_map/templates/user_map/index.html +++ b/user_map/templates/user_map/index.html @@ -27,8 +27,14 @@ {{ user_menu_button | safe }} {{ information_modal | safe }} {{ data_privacy_content | safe }} - {{ legend | safe }} - {{ user_form_template | safe }} + + {% if messages %} + + {% endif %} {% endblock main_content %} @@ -69,5 +75,11 @@ users_layer = L.markerClusterGroup(); addUsers(usermap_list_url, users_layer, marker_icon); users_layer.addTo(map); + + var messages = $('div.messages').html(); + if (messages) + showInformationModal('Information', messages); + + {% endblock js_container %} diff --git a/user_map/templates/user_map/user_info_popup_content.html b/user_map/templates/user_map/user_info_popup_content.html index a4a0bfb..199da8a 100644 --- a/user_map/templates/user_map/user_info_popup_content.html +++ b/user_map/templates/user_map/user_info_popup_content.html @@ -9,12 +9,8 @@

{{ detail }}

{% endfor %} - {% for inasafe_role in user.inasafe_roles.all %} - {% if inasafe_role.name == "Trainer" and user.is_certified_inasafe_trainer %} - - {% else %} - - {% endif %} + {% for role in user.roles.all %} + {% endfor %} {% endspaceless %} diff --git a/user_map/urls.py b/user_map/urls.py index bef6a73..ab05b46 100644 --- a/user_map/urls.py +++ b/user_map/urls.py @@ -19,7 +19,7 @@ url(r'^download$', user_map.views.download, name='download'), ] -# expose static files and uploded media if DEBUG is active +# expose static files and uploaded media if DEBUG is active if settings.DEBUG: urlpatterns += patterns( '', diff --git a/user_map/views.py b/user_map/views.py index 7f87878..60eed67 100644 --- a/user_map/views.py +++ b/user_map/views.py @@ -4,7 +4,7 @@ from exceptions import AttributeError from django.shortcuts import render, render_to_response -from django.http import HttpResponse, HttpResponseRedirect, Http404 +from django.http import HttpResponse, HttpResponseRedirect from django.template import RequestContext, loader from django.core.urlresolvers import reverse from django.contrib import messages @@ -42,8 +42,8 @@ def get(self, request): ) leaflet_tiles = dict( - url=LEAFLET_TILES[1], - attribution=LEAFLET_TILES[2] + url=LEAFLET_TILES[0][1], + attribution=LEAFLET_TILES[0][2] ) context = { @@ -73,13 +73,19 @@ class UserAddView(CreateView): model = UserMap form_class = UserMapForm template_name = 'user_map/add_user.html' + # success_url = reverse('user_map:index') def form_valid(self, form): form.instance.user = self.request.user + messages.add_message( + self.request, + messages.INFO, + 'You have been added successfully to the map!' + ) return super(UserAddView, self).form_valid(form) def get_success_url(self): - return reverse('user_map:add') + return reverse('user_map:index') def dispatch(self, request, *args, **kwargs): try: From 9ec54345fc4cc7e347168ec995fab7b97682c2a4 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Sun, 21 Feb 2016 23:02:44 +0100 Subject: [PATCH 03/36] Fix update profile. --- user_map/models/__init__.py | 1 - user_map/models/user.py | 13 ++- user_map/models/user_manager.py | 110 ------------------ user_map/static/user_map/css/user-map.css | 4 + user_map/templates/user_map/information.html | 15 --- .../{add_user.html => user_add_update.html} | 2 +- .../user_map/user_info_popup_content.html | 4 +- .../templates/user_map/user_menu_button.html | 2 +- user_map/templates/user_map/users.json | 18 --- user_map/urls.py | 6 +- user_map/utilities/decorators.py | 1 + user_map/utilities/utilities.py | 1 + user_map/views.py | 102 ++++++++-------- 13 files changed, 70 insertions(+), 209 deletions(-) delete mode 100644 user_map/models/user_manager.py delete mode 100644 user_map/templates/user_map/information.html rename user_map/templates/user_map/{add_user.html => user_add_update.html} (87%) delete mode 100644 user_map/templates/user_map/users.json diff --git a/user_map/models/__init__.py b/user_map/models/__init__.py index 2f384a1..3b65a35 100644 --- a/user_map/models/__init__.py +++ b/user_map/models/__init__.py @@ -1,3 +1,2 @@ from user_map.models.user import * from user_map.models.role import * -from user_map.models.user_manager import * diff --git a/user_map/models/user.py b/user_map/models/user.py index a699691..3057fd9 100644 --- a/user_map/models/user.py +++ b/user_map/models/user.py @@ -75,12 +75,13 @@ class Meta: def save(self, *args, **kwargs): # """Override save method.""" - # if self.pk: - # # Saving a not new object - # user = UserMap.objects.get(pk=self.pk) - # # Remove the old image if it's new image - # if user.image != self.image: - # user.image.delete(save=False) + is_new = not bool(UserMap.objects.filter(pk=self.pk).count()) + if not is_new: + # Saving a not new object + usermap = UserMap.objects.get(pk=self.pk) + # Remove the old image if it's new image + if usermap.image != self.image: + usermap.image.delete(save=False) # Wrap location data self.location.x = wrap_number(self.location.x, [-180, 180]) diff --git a/user_map/models/user_manager.py b/user_map/models/user_manager.py deleted file mode 100644 index 9f5c4b1..0000000 --- a/user_map/models/user_manager.py +++ /dev/null @@ -1,110 +0,0 @@ -# # coding=utf-8 -# """Custom User Manager for user of InaSAFE User Map.""" -# from django.contrib.gis.db.models import GeoManager -# from django.contrib.auth.models import BaseUserManager -# from django.contrib.gis.geos import Point -# from django.utils.crypto import get_random_string -# -# from user_map.models.role import Role -# -# -# class CustomUserManager(BaseUserManager, GeoManager): -# """Custom user manager for user map.""" -# class Meta: -# """Meta class.""" -# app_label = 'user_map' -# -# def create_user( -# self, -# name, -# email, -# location, -# role, -# email_updates, -# website='', -# password=None): -# """Create and save a User. -# -# :param name: The name of the user. -# :type name: str -# -# :param email: The email of the user. -# :type email: str -# -# :param location: The location of the user in (long, lat) -# :type location: Point -# -# :param role: The role of the user. -# :type role: Role -# -# :param email_updates: The status email_updates of the user. -# :type email_updates: bool -# -# :param website: The website of the user. -# :type website: str -# -# :param password: The password of the user. -# :type password: str -# """ -# if not name: -# raise ValueError('User must have name.') -# -# if not email: -# raise ValueError('User must have an email address.') -# -# if not location: -# raise ValueError('User must have location.') -# -# if not role: -# raise ValueError('User must have role.') -# -# if not email_updates: -# raise ValueError('User must have email_updates status.') -# -# user = self.model( -# name=name, -# email=self.normalize_email(email), -# location=location, -# role=role, -# email_updates=email_updates, -# website=website, -# key=get_random_string() -# ) -# -# user.set_password(password) -# user.save(using=self._db) -# return user -# -# def create_superuser(self, name, email, password): -# """Create and save a superuser. -# -# :param name: The name of the superuser. -# :type name: str -# -# :param email: The email of the superuser. -# :type email: str -# -# :param password: The password of the superuser. -# :type password: str -# """ -# # Use predefined location, role, email_updates, is_active, is_admin -# location = Point(106.8, -6.2) -# try: -# role = Role.objects.get(name='Super User') -# except Role.DoesNotExist: -# role = Role(name='Super User', sort_number=-999) -# role.save() -# -# user = self.create_user( -# name, -# email, -# location=location, -# role=role, -# email_updates=True, -# password=password) -# user.email_updates = True -# user.is_confirmed = True -# user.is_active = True -# user.is_admin = True -# user.save(using=self._db) -# return user diff --git a/user_map/static/user_map/css/user-map.css b/user_map/static/user_map/css/user-map.css index 161cde0..80e33ae 100644 --- a/user_map/static/user_map/css/user-map.css +++ b/user_map/static/user_map/css/user-map.css @@ -85,3 +85,7 @@ html, body, #map { line-height: 18px; color: #555; } + +.leaflet-popup-content p { + margin: 5px 0px !important; +} diff --git a/user_map/templates/user_map/information.html b/user_map/templates/user_map/information.html deleted file mode 100644 index b649fa9..0000000 --- a/user_map/templates/user_map/information.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "user_map/base_page.html" %} - -{% block main_content %} -
- - {{ information }} -
-{% endblock main_content %} diff --git a/user_map/templates/user_map/add_user.html b/user_map/templates/user_map/user_add_update.html similarity index 87% rename from user_map/templates/user_map/add_user.html rename to user_map/templates/user_map/user_add_update.html index 35a3c74..d4864f9 100644 --- a/user_map/templates/user_map/add_user.html +++ b/user_map/templates/user_map/user_add_update.html @@ -11,7 +11,7 @@ {% block main_content %}
diff --git a/user_map/templates/user_map/user_info_popup_content.html b/user_map/templates/user_map/user_info_popup_content.html index 199da8a..bf8bd62 100644 --- a/user_map/templates/user_map/user_info_popup_content.html +++ b/user_map/templates/user_map/user_info_popup_content.html @@ -6,11 +6,11 @@ {% endif %} {% for detail in user_detail %} -

{{ detail }}

+

{{ detail }}

{% endfor %} {% for role in user.roles.all %} - + {% endfor %}
{% endspaceless %} diff --git a/user_map/templates/user_map/user_menu_button.html b/user_map/templates/user_map/user_menu_button.html index 432284d..b7c5bcd 100644 --- a/user_map/templates/user_map/user_menu_button.html +++ b/user_map/templates/user_map/user_menu_button.html @@ -9,7 +9,7 @@ {% else %} {% endif %} - diff --git a/user_map/urls.py b/user_map/urls.py index 8ab7a4e..2d415cf 100644 --- a/user_map/urls.py +++ b/user_map/urls.py @@ -8,8 +8,6 @@ urlpatterns = [ url(r'^$', user_map.views.IndexView.as_view(), name='index'), - url(r'^users/$', user_map.views.UserMapList.as_view(), - name='usermap-list'), url(r'^add', login_required( user_map.views.UserAddView.as_view(), @@ -20,7 +18,8 @@ user_map.views.UserUpdateView.as_view(), login_url=settings.USER_MAP['login_view']), name='update'), - url(r'^download$', user_map.views.download, name='download'), + url(r'^usermaps/$', user_map.views.UserMapList.as_view(), + name='usermap-list'), ] # expose static files and uploaded media if DEBUG is active diff --git a/user_map/views.py b/user_map/views.py index 01c0c54..0343662 100644 --- a/user_map/views.py +++ b/user_map/views.py @@ -140,35 +140,3 @@ def get_context_data(self, **kwargs): context['description'] = ('Hey %s, please change the information you ' 'want to update below!') % self.request.user return context - - -def download(request): - """The view to download users data as CSV. - - :param request: A django request object. - :type request: request - - :return: A CSV file. - :type: HttpResponse - """ - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="users.csv"' - - users = User.objects.filter(role__sort_number__gte=1) - writer = csv.writer(response) - - fields = ['name', 'website', 'role', 'location'] - headers = ['No.'] - for field in fields: - verbose_name_field = users.model._meta.get_field(field).verbose_name - headers.append(verbose_name_field) - writer.writerow(headers) - - for idx, user in enumerate(users): - row = [idx + 1] - for field in fields: - field_value = getattr(user, field) - row.append(field_value) - writer.writerow(row) - - return response From 764019a8ec5ea0e7efa81fe27c2a4f8a3c9e0ad7 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Mon, 22 Feb 2016 19:18:17 +0100 Subject: [PATCH 05/36] Fix clustering style --- .../static/user_map/css/MarkerCluster.user-map.css | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/user_map/static/user_map/css/MarkerCluster.user-map.css b/user_map/static/user_map/css/MarkerCluster.user-map.css index 993be57..577e427 100644 --- a/user_map/static/user_map/css/MarkerCluster.user-map.css +++ b/user_map/static/user_map/css/MarkerCluster.user-map.css @@ -1,21 +1,21 @@ -.marker-cluster-role1 { +.marker-cluster-small { background-color: rgba(253, 156, 115, 0.6); } -.marker-cluster-role1 div { +.marker-cluster-small div { background-color: rgba(241, 128, 23, 0.6); } -.marker-cluster-role2 { +.marker-cluster-medium { background-color: rgba(181, 226, 140, 0.6); } -.marker-cluster-role2 div { +.marker-cluster-medium div { background-color: rgba(110, 204, 57, 0.6); } -.marker-cluster-role3 { +.marker-cluster-large { background-color: rgba(0, 180, 255, 0.6); } -.marker-cluster-role3 div { +.marker-cluster-large div { background-color: rgba(0, 210, 255, 0.6); } From 9fcf2687c17406ba406dddf2bcbfe1b5ca4b2c47 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Mon, 22 Feb 2016 19:41:22 +0100 Subject: [PATCH 06/36] Remove unused codes. --- user_map/admin.py | 26 +--- user_map/app_settings.py | 8 +- user_map/auth_backend.py | 44 ------- user_map/static/user_map/js/user-map.js | 27 +--- .../templates/user_map/account/edit_user.html | 124 ------------------ .../templates/user_map/account/login.html | 39 ------ .../account/password_reset_complete.html | 16 --- .../account/password_reset_confirm.html | 31 ----- .../user_map/account/password_reset_done.html | 19 --- .../account/password_reset_email.html | 9 -- .../user_map/account/password_reset_form.html | 27 ---- .../user_map/account/registration.html | 40 ------ .../registration_confirmation_email.html | 19 --- user_map/templates/user_map/base.html | 5 - user_map/templates/user_map/data_privacy.html | 9 +- user_map/templates/user_map/legend.html | 8 -- user_map/utilities/utilities.py | 1 - 17 files changed, 10 insertions(+), 442 deletions(-) delete mode 100644 user_map/auth_backend.py delete mode 100644 user_map/templates/user_map/account/edit_user.html delete mode 100644 user_map/templates/user_map/account/login.html delete mode 100644 user_map/templates/user_map/account/password_reset_complete.html delete mode 100644 user_map/templates/user_map/account/password_reset_confirm.html delete mode 100644 user_map/templates/user_map/account/password_reset_done.html delete mode 100644 user_map/templates/user_map/account/password_reset_email.html delete mode 100644 user_map/templates/user_map/account/password_reset_form.html delete mode 100644 user_map/templates/user_map/account/registration.html delete mode 100644 user_map/templates/user_map/account/registration_confirmation_email.html delete mode 100644 user_map/templates/user_map/legend.html diff --git a/user_map/admin.py b/user_map/admin.py index f7c86e0..bbeda57 100644 --- a/user_map/admin.py +++ b/user_map/admin.py @@ -1,9 +1,8 @@ - # coding=utf-8 +# coding=utf-8 """Model Admin Class.""" from django.contrib.gis import admin -from django.contrib.auth import get_user_model -from django.contrib.auth.admin import UserAdmin as BaseUserAdmin + from leaflet.admin import LeafletGeoAdmin from user_map.models import UserMap, Role @@ -13,27 +12,6 @@ class UserMapAdmin(LeafletGeoAdmin): """Admin Class for User Map Model.""" model = UserMap can_delete = False - # list_display = ('name', 'email', 'role', 'website', 'email_updates', - # 'last_login', 'is_confirmed', 'is_admin') - # list_filter = ['role', 'is_confirmed', 'is_admin'] - # search_fields = ['name', 'email'] - # fieldsets = [ - # ('Basic Information', { - # 'fields': [ - # 'name', 'email', 'website', 'role', 'email_updates']}), - # ('Location', {'fields': ['location']}), - # ('Advanced Information', { - # 'fields': ['is_confirmed', 'is_active', 'is_admin', 'last_login'] - # }), - # ] - -from django.contrib.auth.models import User - -# class UserAdmin(BaseUserAdmin): -# """Define the new User Admin plugging the UserMap.""" -# inlines = (UserMapAdmin, ) -# Re-register User Admin -# admin.site.unregister(get_user_model()) admin.site.register(UserMap, UserMapAdmin) admin.site.register(Role) diff --git a/user_map/app_settings.py b/user_map/app_settings.py index a3a1a95..fdb281f 100644 --- a/user_map/app_settings.py +++ b/user_map/app_settings.py @@ -4,14 +4,11 @@ ..note: By design, you can override these settings from your project's settings.py with prefix 'USER_MAP' on the variable e.g 'USER_MAP_USER_ICONS'. - - For mailing. as the default, it wil use 'DEFAULT_FROM_MAIL' setting from - the project. """ from django.conf import settings -# USER_MODEL: The auth user model set in project's settings +# USER_MODEL: The auth user model is set in project's settings USER_MODEL = settings.AUTH_USER_MODEL # USER MAP Settings @@ -30,7 +27,8 @@ 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', # The tile URL ('© OpenStreetMap and contributors, under an ' + 'target="_parent">OpenStreetMap and contributors, ' + 'under an ' 'open license') # The attribution )] diff --git a/user_map/auth_backend.py b/user_map/auth_backend.py deleted file mode 100644 index 87cdccc..0000000 --- a/user_map/auth_backend.py +++ /dev/null @@ -1,44 +0,0 @@ -# coding=utf-8 -"""Authentication backend class for InaSAFE User Map.""" -from user.models.user import User - - -class UserMapAuthBackend(object): - """A custom authentication backend for InaSAFE User Map. - - It allows users to log in using their email address. - """ - def get_user(self, user_id): - """Get user based on the id. - - :param user_id: The id of the user record. In our backend, this should - be an email address. - :type user_id: str - - :return: The user object if exist, otherwise None. - """ - try: - user = User.objects.get(pk=user_id) - if user.is_active: - return user - return None - except User.DoesNotExist: - return None - - def authenticate(self, email=None, password=None): - """Authentication method. - - :param email: The email of the user. - :type email: str - - :param password: The password of the user. - :type password: str - - :return: The user object if it is authenticated successfully. - """ - try: - user = User.objects.get(email=email) - if user.check_password(password): - return user - except User.DoesNotExist: - return None diff --git a/user_map/static/user_map/js/user-map.js b/user_map/static/user_map/js/user-map.js index 5ea26dc..02076bd 100644 --- a/user_map/static/user_map/js/user-map.js +++ b/user_map/static/user_map/js/user-map.js @@ -9,13 +9,14 @@ /** * Add users to the respective layer based on role. * @param {string} url The url view to get users. - * @param {object} role The role object. + * @param {object} users_layer Passed users' layer + * @param {object} icon The icon object for marker * @name L The Class from Leaflet. * @property geoJson Property of L class. * @property users Property of response object. * @function addTo add child element to the map. * @property properties Property of a feature. - * @property popupContent Property of properties. + * @property popup_content property of properties. * @function bindPopup Bind popup to marker */ function addUsers(url, users_layer, icon) { @@ -24,15 +25,6 @@ function addUsers(url, users_layer, icon) { url: url, dataType: 'json', success: function (response) { - //L.geoJson( - // response, - // { - // onEachFeature: onEachFeature, - // pointToLayer: function (feature, latlng) { - // //return L.marker(latlng,{icon: role['icon'] }); - // return L.marker(latlng); - // } - // }).addTo(users_layer); L.geoJson(response, { onEachFeature: function (feature, layer) { layer.bindPopup(feature.properties.popup_content); @@ -43,13 +35,6 @@ function addUsers(url, users_layer, icon) { }).addTo(users_layer) } }); - //function onEachFeature(feature, layer) { - // // Set the popup content if it does have the content - // if (feature.properties && feature.properties.popupContent) { - // //layer.bindPopup(feature.properties.popupContent); - // layer.bindPopup(feature.properties.username); - // } - //} } /** @@ -160,15 +145,9 @@ function createUserMenuControl(options) { if (options.indexOf('edit') != -1) user_menu_container.innerHTML += $("#user-menu-edit-button").html(); - if (options.indexOf('delete') != -1) - user_menu_container.innerHTML += $("#user-menu-delete-button").html(); - if (options.indexOf('api') != -1) user_menu_container.innerHTML += $("#user-menu-api-button").html(); - if (options.indexOf('forgot') != -1) - user_menu_container.innerHTML += $("#user-menu-forgot-button").html(); - //Prevent firing drag and onClickMap event when clicking this control var stop = L.DomEvent.stopPropagation; L.DomEvent diff --git a/user_map/templates/user_map/account/edit_user.html b/user_map/templates/user_map/account/edit_user.html deleted file mode 100644 index da61661..0000000 --- a/user_map/templates/user_map/account/edit_user.html +++ /dev/null @@ -1,124 +0,0 @@ -{% extends "user_map/base_page.html" %} -{% load leaflet_tags %} -{% load bootstrap %} - -{% block head_resources %} - {{ block.super }} - {% leaflet_js plugins="forms" %} - {% leaflet_css plugins="forms" %} -{% endblock head_resources %} - -{% block main_content %} -
- - -
-
- -
-
- -
-
- {% if messages %} - {% for message in messages %} - {% if 'success' in message.tags %} -
- x - {{ message }} -
- {% endif %} - {% endfor %} - {% endif %} - -
-
-
-

Basic Information

-
-
- - {% csrf_token %} - {{ basic_info_form|bootstrap_horizontal:'col-lg-3'}} -
-
- -
-
- -
-
-
- -
-
-
-

Change Password

-
-
-
- {% csrf_token %} - {{ change_password_form|bootstrap_horizontal:'col-lg-3'}} -
-
- -
-
-
-
-
-
- -
-
-
-

Delete Account

-
-
-

- Once you delete your account, you will be no longer available - on the map. However, you can register again any time you - want! -

-
- {% csrf_token %} - -
-
-
-
-
-
-
-{% endblock main_content %} - -{% block js_container %} - -{% endblock js_container %} diff --git a/user_map/templates/user_map/account/login.html b/user_map/templates/user_map/account/login.html deleted file mode 100644 index 33d9b6e..0000000 --- a/user_map/templates/user_map/account/login.html +++ /dev/null @@ -1,39 +0,0 @@ -{% extends "user_map/base_page.html" %} -{% load leaflet_tags %} -{% load bootstrap %} - -{% block head_resources %} - {{ block.super }} - {% leaflet_js plugins="forms" %} - {% leaflet_css plugins="forms" %} -{% endblock head_resources %} - -{% block main_content %} - {{ block.super }} -
- - -
-{% endblock main_content %} - -{% block js_container %} - {{ block.super }} -{% endblock js_container %} diff --git a/user_map/templates/user_map/account/password_reset_complete.html b/user_map/templates/user_map/account/password_reset_complete.html deleted file mode 100644 index c83b429..0000000 --- a/user_map/templates/user_map/account/password_reset_complete.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'user_map/base_page.html' %} - -{% block main_content %} -
-
- -
- -

Your password has been reset. You may go ahead and log in now.

-

Log in

- -
-{% endblock main_content%} - diff --git a/user_map/templates/user_map/account/password_reset_confirm.html b/user_map/templates/user_map/account/password_reset_confirm.html deleted file mode 100644 index aaacc82..0000000 --- a/user_map/templates/user_map/account/password_reset_confirm.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends 'user_map/base_page.html' %} -{% load bootstrap %} - -{% block title %}Reset Password{% endblock %} - -{% block main_content %} -
-
- -
- - {% if validlink %} -
-
- {% csrf_token %} - {{ form|bootstrap_horizontal:'col-lg-3'}} -
-
- -
-
-
-
- {% else %} -

Ooops! This reset link is no longer valid!

- {% endif %} -
-{% endblock main_content %} diff --git a/user_map/templates/user_map/account/password_reset_done.html b/user_map/templates/user_map/account/password_reset_done.html deleted file mode 100644 index ed38bee..0000000 --- a/user_map/templates/user_map/account/password_reset_done.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends 'user_map/base_page.html' %} - -{% block main_content %} -
-
- -
- -

We've e-mailed you instructions for setting your password to the e-mail address you submitted.

-

You should be receiving it shortly.

-
-

- If you don't receive an email, please make sure you've entered the - address you registered with, and check your spam folder. -

-
-{% endblock main_content%} diff --git a/user_map/templates/user_map/account/password_reset_email.html b/user_map/templates/user_map/account/password_reset_email.html deleted file mode 100644 index c5d627c..0000000 --- a/user_map/templates/user_map/account/password_reset_email.html +++ /dev/null @@ -1,9 +0,0 @@ -{% autoescape off %} -Hi {{ user.name}}, - -You're receiving this email because you requested a password reset for your user account at {{ site_name }} -Please go to the following page to set a new password: -{{ protocol}}://{{ domain }}{% url 'user_map:password_reset_confirm' uidb64=uid token=token %} - -Thanks for using our site! -{% endautoescape %} diff --git a/user_map/templates/user_map/account/password_reset_form.html b/user_map/templates/user_map/account/password_reset_form.html deleted file mode 100644 index 3e0399d..0000000 --- a/user_map/templates/user_map/account/password_reset_form.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends 'user_map/base_page.html' %} -{% load bootstrap %} - -{% block title %}Reset Password {% endblock %} - -{% block main_content %} -
-
- -
- -
-
- {% csrf_token %} - {{ form|bootstrap_horizontal:'col-lg-2'}} -
-
- -
-
-
-
-
-{% endblock main_content %} diff --git a/user_map/templates/user_map/account/registration.html b/user_map/templates/user_map/account/registration.html deleted file mode 100644 index 98fca09..0000000 --- a/user_map/templates/user_map/account/registration.html +++ /dev/null @@ -1,40 +0,0 @@ -{% extends "user_map/base_page.html" %} -{% load leaflet_tags %} -{% load bootstrap %} - -{% block head_resources %} - {{ block.super }} - {% leaflet_js plugins="forms" %} - {% leaflet_css plugins="forms" %} -{% endblock head_resources %} - -{% block main_content %} -
- -
- {% if messages %} - {% for message in messages %} - {% if 'success' in message.tags %} -
- x - {{ message }} -
- {% endif %} - {% endfor %} - {% endif %} -
- {% csrf_token %} - {{ form|bootstrap_horizontal:'col-lg-3'}} -
-
- -
-
-
-
-
-{% endblock main_content %} diff --git a/user_map/templates/user_map/account/registration_confirmation_email.html b/user_map/templates/user_map/account/registration_confirmation_email.html deleted file mode 100644 index 9724de3..0000000 --- a/user_map/templates/user_map/account/registration_confirmation_email.html +++ /dev/null @@ -1,19 +0,0 @@ -Dear {{ user.name }}, - -Thank you for adding yourself to the {{ project_name }} User Map at -{{ site_name }}. To activate your account, please click this link: -{{ protocol}}://{{ domain }}{% url 'user_map:confirm_registration' uid=uid key=key %} - -Please note that the following information in your user record will be freely downloadable -by anyone visiting the user map page: - - Your name - - your location (longitude, latitude) - - your role (user, developer, trainer) - -If you are concerned about your privacy, we recommend you edit your record (you can -do so using the link provided below) and generalise the position of your point (for example -to the centre of your nearest town / state or province or country). - -Thank you for taking the time to register yourself on our site! - -Regards diff --git a/user_map/templates/user_map/base.html b/user_map/templates/user_map/base.html index e0f4655..6415e3f 100644 --- a/user_map/templates/user_map/base.html +++ b/user_map/templates/user_map/base.html @@ -25,11 +25,6 @@ -{#
#} -{# {% block navbar %}#} -{# {% endblock navbar %}#} -{#
#} - {% block main_content %} {% endblock main_content %} diff --git a/user_map/templates/user_map/data_privacy.html b/user_map/templates/user_map/data_privacy.html index 93136d3..d98523b 100644 --- a/user_map/templates/user_map/data_privacy.html +++ b/user_map/templates/user_map/data_privacy.html @@ -2,11 +2,6 @@ The data you enter on this site may be visible to others. We suggest that you approximate your physical location to the nearest town or major center. Parts of your data will be made available for - others to download and use. We will not share:
-
    -
  • your email address
  • -
  • your password
  • -
- If you would like your data removed from this site, please log in to your - account. + others to download and use. Click the REST API link to see the data that are + available for public to use. diff --git a/user_map/templates/user_map/legend.html b/user_map/templates/user_map/legend.html deleted file mode 100644 index e07498f..0000000 --- a/user_map/templates/user_map/legend.html +++ /dev/null @@ -1,8 +0,0 @@ -{% load staticfiles %} - - diff --git a/user_map/utilities/utilities.py b/user_map/utilities/utilities.py index 3e4860f..c1e447a 100644 --- a/user_map/utilities/utilities.py +++ b/user_map/utilities/utilities.py @@ -1,6 +1,5 @@ # coding=utf-8 """Utilities module.""" -from django.core.exceptions import ObjectDoesNotExist def wrap_number(number, range): From 6924688e41902ce9981f0f8ac0f29e9a27856b45 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Mon, 22 Feb 2016 20:35:37 +0100 Subject: [PATCH 07/36] Add management command to update roles. This command should be called after changing the roles in settings in the case that you have called migrate (roles table is already filled with some data) Bear in mind that you should keep the ids the same and just change the name or the badge path. And this command will only update the role table, not the usermap table that uses the role table. So, adding roles is fine (as long as the new ids are unique). Removing roles has no effect (it will not remove the row in the table). Well, it could give you problems as it means that some of the users might not have valid roles defined in the role table. How to use this: python manage.py update_roles --- user_map/management/__init__.py | 0 user_map/management/commands/__init__.py | 0 user_map/management/commands/update_roles.py | 56 ++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 user_map/management/__init__.py create mode 100644 user_map/management/commands/__init__.py create mode 100644 user_map/management/commands/update_roles.py diff --git a/user_map/management/__init__.py b/user_map/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user_map/management/commands/__init__.py b/user_map/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user_map/management/commands/update_roles.py b/user_map/management/commands/update_roles.py new file mode 100644 index 0000000..99ba119 --- /dev/null +++ b/user_map/management/commands/update_roles.py @@ -0,0 +1,56 @@ +# coding=utf-8 +from django.core.management.base import BaseCommand + +from user_map.app_settings import ROLES +from user_map.models.role import Role + + +class Command(BaseCommand): + help = 'Update the roles in the database' + + def handle(self, *args, **options): + """This command should be called after changing the roles in + settings in the case that you have called migrate (roles table is + already filled with some data) + + Bear in mind that you should keep the ids the same and just change + the name or the badge path. And this command will only update the role + table, not the usermap table that uses the role table. + + So, adding roles is fine (as long as the new ids are unique). Removing + roles has no effect (it will not remove the row in the table). Well, it + could give you problems as it means that some of the users might not + have valid roles defined in the role table. + + How to use this: + python manage.py update_roles + """ + for new_role in ROLES: + try: + role = Role.objects.get(pk=new_role['id']) + old_name = role.name + old_badge = role.badge + role.name = new_role['name'] + role.badge = new_role['badge'] + role.save() + self.stdout.write( + 'Role (%s, %s, %s) has been updated to (%s, %s, %s)' % ( + role.id, old_name, old_badge, + role.id, role.name, role.badge + ) + ) + except Role.DoesNotExist: + # It means a new role + role = Role.objects.create( + id=new_role['id'], + name=new_role['name'], + badge=new_role['badge'] + ) + self.stdout.write( + 'New Role (%s, %s, %s) has been added' % ( + role.id, role.name, role.badge, + ) + ) + + + self.stdout.write('> The operation update_role is run successfully.') From c641fb78f38329fdeca0682cf67c18379688b1a5 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Wed, 24 Feb 2016 00:07:30 +0100 Subject: [PATCH 08/36] Fix tests for role model. --- user_map/tests/model_factories.py | 20 ++++++++++++++------ user_map/tests/test_models.py | 30 +++++++++++++++++++++--------- user_map/tests/test_views.py | 2 +- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/user_map/tests/model_factories.py b/user_map/tests/model_factories.py index 76fe636..f1bdcb7 100644 --- a/user_map/tests/model_factories.py +++ b/user_map/tests/model_factories.py @@ -1,10 +1,18 @@ # coding=utf-8 """Model factories definition for models.""" +from django.conf import settings from django.contrib.gis.geos import Point import factory from factory import DjangoModelFactory -from user.models import Role, User +from user_map.models import Role, UserMap + + +class UserFactory(DjangoModelFactory): + class Meta: + model = settings.AUTH_USER_MODEL + + username = factory.Sequence(lambda n: 'user{0}'.format(n)) class RoleFactory(DjangoModelFactory): @@ -13,16 +21,16 @@ class Meta: """Meta definition.""" model = Role + id = factory.Sequence(lambda n: n) name = factory.Sequence(lambda n: 'Role %s' % n) - sort_number = 1 + badge = factory.Sequence(lambda n: '/path/to/badge/role%s' % n) -class UserFactory(DjangoModelFactory): - """Factory class for User Model""" +class UserMapFactory(DjangoModelFactory): + """Factory class for UserMap Model""" class Meta: """"Meta definition.""" - model = User - django_get_or_create = ('email',) + model = UserMap # Taking others as default value defined in model but not these: name = 'John Doe' diff --git a/user_map/tests/test_models.py b/user_map/tests/test_models.py index a93c0c5..ad867a2 100644 --- a/user_map/tests/test_models.py +++ b/user_map/tests/test_models.py @@ -1,12 +1,15 @@ # coding=utf-8 """Module related to test for all the models.""" -from django.test import TestCase +from django.test import TransactionTestCase -from user.tests.model_factories import RoleFactory, UserFactory +from user_map.tests.model_factories import RoleFactory, UserMapFactory +from user_map.models.role import Role -class TestRole(TestCase): +class TestRole(TransactionTestCase): """Class to test Role model.""" + reset_sequences = True + def setUp(self): pass @@ -18,11 +21,20 @@ def test_create_role(self): def test_read_role(self): """Method to test reading role.""" - role_name = 'Testing Role' - role = RoleFactory.create(name=role_name) - message = 'The role name should be %s, but it gives %s' % ( - role_name, role.name) - self.assertEqual(role_name, role.name, message) + for i in range(3): + RoleFactory(__sequence=i) + + for i in range(3): + role_name = 'Role %s' % i + role_badge = '/path/to/badge/role%s' % i + + role = Role.objects.get(pk=i) + message = 'The role name should be %s, but it gives %s' % ( + role_name, role.name) + self.assertEqual(role_name, role.name, message) + message = 'The role badge should be %s, but it gives %s' % ( + role_badge, role.badge) + self.assertEqual(role_badge, role.badge, message) def test_update_role(self): """Method to test updating role.""" @@ -43,7 +55,7 @@ def test_delete_role(self): self.assertIsNone(role.id, message) -class TestUser(TestCase): +class TestUser(TransactionTestCase): """Class to test User model.""" def setUp(self): pass diff --git a/user_map/tests/test_views.py b/user_map/tests/test_views.py index 5e8ac1b..6eecb87 100644 --- a/user_map/tests/test_views.py +++ b/user_map/tests/test_views.py @@ -6,7 +6,7 @@ from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes -from user.tests.model_factories import UserFactory +from user_map.tests.model_factories import UserFactory class UserMapViewTests(TestCase): From 19fe76202a25d28882ca4f15775280940196192f Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Wed, 24 Feb 2016 16:22:49 +0100 Subject: [PATCH 09/36] Fix User Map model tests --- user_map/tests/model_factories.py | 23 +++++-- user_map/tests/test_models.py | 104 ++++++++++++++++-------------- 2 files changed, 73 insertions(+), 54 deletions(-) diff --git a/user_map/tests/model_factories.py b/user_map/tests/model_factories.py index f1bdcb7..2a2f686 100644 --- a/user_map/tests/model_factories.py +++ b/user_map/tests/model_factories.py @@ -2,6 +2,7 @@ """Model factories definition for models.""" from django.conf import settings from django.contrib.gis.geos import Point + import factory from factory import DjangoModelFactory @@ -23,7 +24,8 @@ class Meta: id = factory.Sequence(lambda n: n) name = factory.Sequence(lambda n: 'Role %s' % n) - badge = factory.Sequence(lambda n: '/path/to/badge/role%s' % n) + badge = factory.Sequence( + lambda n: settings.BASE_DIR + '/path/to/badge/role%s' % n) class UserMapFactory(DjangoModelFactory): @@ -33,9 +35,18 @@ class Meta: model = UserMap # Taking others as default value defined in model but not these: - name = 'John Doe' - email = factory.Sequence(lambda n: 'john.doe%s@example.com' % n) - password = factory.PostGenerationMethodCall( - 'set_password', 'default_password') + user = factory.SubFactory(UserFactory) location = Point(105.567, 123) - role = factory.SubFactory(RoleFactory) + image = factory.Sequence( + lambda n: settings.MEDIA_ROOT + '/path/to/image/user%s' % n) + + @factory.post_generation + def roles(self, create, extracted, **kwargs): + if not create: + # Simple build, do nothing. + return + + if extracted: + # A list of groups were passed in, use them + for role in extracted: + self.roles.add(role) diff --git a/user_map/tests/test_models.py b/user_map/tests/test_models.py index ad867a2..c1f359c 100644 --- a/user_map/tests/test_models.py +++ b/user_map/tests/test_models.py @@ -1,8 +1,11 @@ # coding=utf-8 """Module related to test for all the models.""" +from django.conf import settings from django.test import TransactionTestCase +from django.contrib.gis.geos import Point -from user_map.tests.model_factories import RoleFactory, UserMapFactory +from user_map.tests.model_factories import ( + RoleFactory, UserFactory, UserMapFactory) from user_map.models.role import Role @@ -26,7 +29,7 @@ def test_read_role(self): for i in range(3): role_name = 'Role %s' % i - role_badge = '/path/to/badge/role%s' % i + role_badge = settings.BASE_DIR + '/path/to/badge/role%s' % i role = Role.objects.get(pk=i) message = 'The role name should be %s, but it gives %s' % ( @@ -55,55 +58,60 @@ def test_delete_role(self): self.assertIsNone(role.id, message) -class TestUser(TransactionTestCase): +class TestUserMap(TransactionTestCase): """Class to test User model.""" def setUp(self): pass - def test_create_user(self): - """Method to test user creation.""" - user = UserFactory.create() - message = 'The user is not instantiated successfully.' - self.assertIsNotNone(user.id, message) - - # email_updates, is_admin, is_confirmed = False - message = 'email_updates must be False' - self.assertFalse(user.email_updates, message) - message = 'is_admin must be False' - self.assertFalse(user.is_admin, message) - message = 'is_confirmed must be False' - self.assertFalse(user.is_confirmed, message) - - # is_active = True - message = 'is_active must be True' - self.assertTrue(user.is_active, message) - - def test_read_user(self): - """Method to test reading user.""" - user_name = 'John Doe' - user_website = 'www.johndoe.com' - user = UserFactory.create(name=user_name, website=user_website) - message = 'The user name should be %s, but it gives %s' % ( - user_name, user.name) - self.assertEqual(user_name, user.name, message) - message = 'The user website should be %s, but it gives %s' % ( - user_website, user.website) - self.assertEqual(user_website, user.website, message) - - def test_update_user(self): - """Method to test update user.""" - user = UserFactory.create() - user_name = 'Updated John Doe' - user.name = user_name - user.save() - message = 'The user name should be %s, but it gives %s' % ( - user_name, user.name) - self.assertEqual(user_name, user.name, message) + def test_create_usermap(self): + """Method to test user map creation.""" + user_map = UserMapFactory.create() + message = 'The user map is not instantiated successfully.' + self.assertIsNotNone(user_map.user, message) + + def test_read_usermap(self): + """Method to test reading user map.""" + user = UserFactory(username='John Doe') + location = Point(5, 5) + image = '/john/doe/image.png' + + usermap = UserMapFactory.create( + user=user, location=location, image=image) + + message = 'The username should be %s, but it gives %s' % ( + user.username, usermap.user.username) + self.assertEqual(user.username, usermap.user.username, message) + + message = 'The user location should be %s, but it gives %s' % ( + location, usermap.location) + self.assertEqual(location, usermap.location, message) + + message = 'The user image should be %s, but it gives %s' % ( + image, usermap.image) + self.assertEqual(image, usermap.image, message) + + def test_update_usermap(self): + """Method to test updating usermap.""" + user_map = UserMapFactory() + + new_location = Point(10, 10) + user_map.location = new_location + user_map.save() + message = 'The user map location should be %s, but it gives %s' % ( + new_location, user_map.location) + self.assertEqual(new_location, user_map.location, message) + + new_image = '/new/image.png' + user_map.image = new_image + user_map.save() + message = 'The user map image should be %s, but it gives %s' % ( + new_image, user_map.image) + self.assertEqual(new_image, user_map.image, message) def test_delete_user(self): - """Method to test delete user.""" - user = UserFactory.create() - self.assertIsNotNone(user.id) - user.delete() - message = 'The user is not deleted.' - self.assertIsNone(user.id, message) + """Method to test deleting user map.""" + user_map = UserMapFactory.create() + self.assertIsNotNone(user_map.pk) + user_map.delete() + message = 'The user map is not deleted.' + self.assertIsNone(user_map.pk, message) From ed2929fda26af3e30a8fb35147016fa1a81861ed Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Wed, 24 Feb 2016 16:44:55 +0100 Subject: [PATCH 10/36] Update setup.py and requirements.txt --- requirements.txt | 5 ++++- setup.py | 15 ++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 07ad158..68bb5d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ -Django==1.8.9 +Django>=1.8 django-leaflet==0.18.0 psycopg2==2.6.1 factory-boy==2.6.0 django-bootstrap-form==3.2 +djangorestframework==3.3.2 +djangorestframework-gis==0.10.1 +Pillow==3.1.1 diff --git a/setup.py b/setup.py index ffee6d4..fa65798 100644 --- a/setup.py +++ b/setup.py @@ -23,11 +23,16 @@ 'django web site.'), long_description=open('README.md').read(), install_requires=[ - "Django==1.7", - "django-leaflet==0.14.1", - "psycopg2==2.5.4, - "factory-boy==2.4.1", - "django-bootstrap-form==3.1", + "Django>=1.8", + "django-leaflet", + "psycopg2", + "django-bootstrap-form", + "djangorestframework", + "djangorestframework-gis" + "Pillow" + ], + tests_require=[ + "factory-boy>=2.4.1", ], test_suite='user_map.run_tests.run', ) From 680b4af3a97c1540b8d0b4a3e1e85f26d88ca636 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Wed, 24 Feb 2016 18:37:46 +0100 Subject: [PATCH 11/36] Missing comma in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fa65798..3064c5b 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ "psycopg2", "django-bootstrap-form", "djangorestframework", - "djangorestframework-gis" + "djangorestframework-gis", "Pillow" ], tests_require=[ From b26ec055d8bdefec3327f8c48469163ee8916e10 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Wed, 24 Feb 2016 19:08:15 +0100 Subject: [PATCH 12/36] Update settings for testing. --- user_map/tests/test_settings.py | 49 +++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/user_map/tests/test_settings.py b/user_map/tests/test_settings.py index 9909ef4..aa7aace 100644 --- a/user_map/tests/test_settings.py +++ b/user_map/tests/test_settings.py @@ -14,41 +14,56 @@ } } -INSTALLED_APPS = [ +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', 'django.contrib.contenttypes', - 'django.contrib.sites', 'django.contrib.sessions', + 'django.contrib.messages', 'django.contrib.staticfiles', - 'django.contrib.auth', - 'django.contrib.admin', + 'django.contrib.gis', + 'django.contrib.sites', + 'user_map', 'leaflet', 'bootstrapform', - 'user_map', -] + 'rest_framework', + 'rest_framework_gis', +) ROOT_URLCONF = 'user_map.tests.urls' -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.contrib.auth.context_processors.auth', - 'django.core.context_processors.request', - 'django.contrib.messages.context_processors.messages', - 'user_map.context_processors.user_map_settings', -) +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + + # User map + 'user_map.context_processors.user_map_settings', + ], + }, + }, +] MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', -) + 'django.middleware.security.SecurityMiddleware', -AUTH_USER_MODEL = 'user_map.User' -AUTHENTICATION_BACKENDS = [ - 'user_map.auth_backend.UserMapAuthBackend', - 'django.contrib.auth.backends.ModelBackend'] + 'django.contrib.auth.middleware.AuthenticationMiddleware' +) STATIC_ROOT = local_path('static/') STATIC_URL = '/static/' From 70fdd01a589ad662bede779a6360d7a08402eab4 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Thu, 25 Feb 2016 21:58:33 +0100 Subject: [PATCH 13/36] Fix all the tests. --- user_map/app_settings.py | 4 +- user_map/tests/model_factories.py | 8 +- user_map/tests/test_models.py | 3 +- user_map/tests/test_settings.py | 45 +++++ user_map/tests/test_views.py | 273 +++++++++++------------------- 5 files changed, 149 insertions(+), 184 deletions(-) diff --git a/user_map/app_settings.py b/user_map/app_settings.py index fdb281f..07b67b1 100644 --- a/user_map/app_settings.py +++ b/user_map/app_settings.py @@ -15,7 +15,7 @@ default_setting = { 'project_name': 'InaSAFE', 'favicon_file': 'user_map/img/user-icon.png', - 'login_url': 'login', + 'login_view': 'django.contrib.auth.views.login', 'marker': { 'icon': 'user_map/img/user-icon.png', 'shadow': 'user_map/img/shadow-icon.png' # or 'shadow': None @@ -68,7 +68,7 @@ # LOGIN_VIEW: The view to the login page LOGIN_VIEW = user_map_settings.get( - 'login_url', default_setting['login_url']) + 'login_view', default_setting['login_view']) # MARKER MARKER = user_map_settings.get( diff --git a/user_map/tests/model_factories.py b/user_map/tests/model_factories.py index 2a2f686..421f1c9 100644 --- a/user_map/tests/model_factories.py +++ b/user_map/tests/model_factories.py @@ -14,6 +14,8 @@ class Meta: model = settings.AUTH_USER_MODEL username = factory.Sequence(lambda n: 'user{0}'.format(n)) + password = factory.PostGenerationMethodCall( + 'set_password', 'default_password') class RoleFactory(DjangoModelFactory): @@ -25,7 +27,7 @@ class Meta: id = factory.Sequence(lambda n: n) name = factory.Sequence(lambda n: 'Role %s' % n) badge = factory.Sequence( - lambda n: settings.BASE_DIR + '/path/to/badge/role%s' % n) + lambda n: 'path/to/badge/role%s' % n) class UserMapFactory(DjangoModelFactory): @@ -37,8 +39,8 @@ class Meta: # Taking others as default value defined in model but not these: user = factory.SubFactory(UserFactory) location = Point(105.567, 123) - image = factory.Sequence( - lambda n: settings.MEDIA_ROOT + '/path/to/image/user%s' % n) + # image = factory.Sequence( + # lambda n: 'path/to/image/user%s' % n) @factory.post_generation def roles(self, create, extracted, **kwargs): diff --git a/user_map/tests/test_models.py b/user_map/tests/test_models.py index c1f359c..20b1ca1 100644 --- a/user_map/tests/test_models.py +++ b/user_map/tests/test_models.py @@ -1,6 +1,5 @@ # coding=utf-8 """Module related to test for all the models.""" -from django.conf import settings from django.test import TransactionTestCase from django.contrib.gis.geos import Point @@ -29,7 +28,7 @@ def test_read_role(self): for i in range(3): role_name = 'Role %s' % i - role_badge = settings.BASE_DIR + '/path/to/badge/role%s' % i + role_badge = 'path/to/badge/role%s' % i role = Role.objects.get(pk=i) message = 'The role name should be %s, but it gives %s' % ( diff --git a/user_map/tests/test_settings.py b/user_map/tests/test_settings.py index aa7aace..5ff1299 100644 --- a/user_map/tests/test_settings.py +++ b/user_map/tests/test_settings.py @@ -1,3 +1,4 @@ +# coding=utf-8 import os local_path = lambda path: os.path.join(os.path.dirname(__file__), path) @@ -69,3 +70,47 @@ STATIC_URL = '/static/' SECRET_KEY = 'django-user-map' + + +USER_MAP = { + 'project_name': 'Test Project', + 'favicon_file': '', + 'login_view': 'django.contrib.auth.views.login', + 'marker': { + 'icon': 'user_map/img/user-icon.png', + 'shadow': 'user_map/img/shadow-icon.png', # or 'shadow': None + }, + 'leaflet_config': { + 'TILES': [ + ( + 'OpenStreetMap', # The title + 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', + # The tile URL + ('© OpenStreetMap and contributors, under an ' + 'open license') # The attribution + )] + }, + 'roles': [ + { + 'id': 1, + 'name': 'User', + 'badge': 'user_map/img/inasafe-badge-user.png' + }, + { + 'id': 2, + 'name': 'Trainer', + 'badge': 'user_map/img/inasafe-badge-trainer.png' + }, + { + 'id': 3, + 'name': 'Developer', + 'badge': 'user_map/img/inasafe-badge-developer.png' + } + ], + 'api_user_fields': [ + 'username' + ], + +} diff --git a/user_map/tests/test_views.py b/user_map/tests/test_views.py index 6eecb87..0a20928 100644 --- a/user_map/tests/test_views.py +++ b/user_map/tests/test_views.py @@ -3,145 +3,116 @@ from django.test import TestCase from django.test.client import Client from django.core.urlresolvers import reverse -from django.utils.http import urlsafe_base64_encode -from django.utils.encoding import force_bytes +from django.contrib.gis.geos import Point -from user_map.tests.model_factories import UserFactory +from user_map.tests.model_factories import UserFactory, UserMapFactory +from user_map.app_settings import LOGIN_VIEW +from user_map.models import UserMap class UserMapViewTests(TestCase): """Class for testing user map view.""" def setUp(self): """Run for each test.""" - self.email = 'test@gmail.com' - self.password = 'test' - self.user = UserFactory.create( - email=self.email, - password=self.password, - role__name='Test User', - is_confirmed=True) + self.not_mapped_username = 'test@gmail.com' + self.not_mapped_password = 'test' + self.not_mapped_user = UserFactory.create( + username=self.not_mapped_username, + password=self.not_mapped_password) + + self.mapped_username = 'test2@gmail.com' + self.mapped_password = 'test2' + self.mapped_user = UserFactory.create( + username=self.mapped_username, + password=self.mapped_password) + self.mapped_user_map = UserMapFactory(user=self.mapped_user) + self.client = Client() def test_index(self): """Test for index view.""" response = self.client.get(reverse('user_map:index')) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'user_map/legend.html') self.assertTemplateUsed(response, 'user_map/data_privacy.html') - self.assertContains(response, 'Sign Up') - self.assertContains(response, 'Log In') + self.assertTemplateUsed(response, 'user_map/index.html') - def test_index_login(self): - """Test for index view after logging in.""" + def test_index_login_mapped(self): + """Test for index view after logging in for mapped user.""" self.assertTrue( - self.client.login(email=self.email, password=self.password)) + self.client.login( + username=self.mapped_username, + password=self.mapped_password)) response = self.client.get(reverse('user_map:index')) - self.assertNotContains(response, 'Sign Up') - self.assertNotContains(response, 'Log In') - self.assertContains(response, 'Hi, %s' % self.user.name) - - def test_get_users(self): - """Test for get_users view.""" - response = self.client.get( - reverse('user_map:get_users'), - {'user_role': 'Test User'}) + self.assertNotContains(response, 'user-menu-add-button') + self.assertContains(response, 'user-menu-edit-button') + + def test_list_users(self): + """Test for listing all the users through REST API.""" + response = self.client.get(reverse('user_map:usermap-list')) self.assertEqual(response['Content-Type'], 'application/json') self.assertContains(response, 'FeatureCollection') - self.assertContains(response, self.user.name) + self.assertContains(response, self.mapped_user.username) - def test_get_users_with_post(self): - """Test get_users view.""" - response = self.client.post( - reverse('user_map:get_users'), - {'user_role': 'Test User'}) - self.assertEqual(response.status_code, 404) + def test_list_users_with_post(self): + """Test list user view.""" + response = self.client.post(reverse('user_map:usermap-list')) + self.assertEqual(response.status_code, 405) # 405 = not allowed - def test_show_register_page(self): - """Test register view using get.""" - response = self.client.get(reverse('user_map:register')) - self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'user_map/account/registration.html') + def test_add_user_page_without_login(self): + """Test showing 'add user' page without log in first. - def test_register_success(self): - """Test register view using post.""" - response = self.client.post( - reverse('user_map:register'), - { - 'name': 'John Doe', - 'email': 'john.doe@gmail.com', - 'password': 'password', - 'password2': 'password', - 'website': '', - 'role': '1', - 'location': ('{"type":"Point","coordinates":[22.5,' - '-16.63619187839765]}') - }) + Should redirect to index page""" + response = self.client.get(reverse('user_map:add')) self.assertRedirects( response, - reverse('user_map:register'), + reverse(LOGIN_VIEW) + '?next=/add', 302, 200) - def test_confirm_registration_invalid(self): - """Test confirm_registration using invalid link.""" - response = self.client.get( - reverse('user_map:confirm_registration', args=('l1nk', 'inV4lid'))) - self.assertTemplateUsed(response, 'user_map/information.html') - self.assertContains(response, 'Your link is not valid') - - def test_confirm_registration_valid(self): - """Test confirm_registration using valid link.""" - # Create unconfirmed user first - user = UserFactory.create(is_confirmed=False) - uid = urlsafe_base64_encode(force_bytes(user.pk)) - response = self.client.get( - reverse('user_map:confirm_registration', args=(uid, user.key))) - self.assertTemplateUsed(response, 'user_map/information.html') - self.assertContains( - response, - 'Congratulations! Your account has been successfully confirmed.') - - def test_show_login_page(self): - """Test if showing login page is OK.""" - response = self.client.get(reverse('user_map:login')) + def test_add_user_page_with_unmapped_user(self): + """Test showing 'add user' view using get.""" + # login with unmapped user + self.assertTrue( + self.client.login( + username=self.not_mapped_username, + password=self.not_mapped_password)) + response = self.client.get(reverse('user_map:add')) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'user_map/account/login.html') - - def test_login_invalid(self): - """Test login with invalid user.""" - response = self.client.post( - reverse('user_map:login'), - { - 'email': 'invalid@user.com', - 'password': 'invaliduserpass' - } - ) - self.assertTemplateUsed(response, 'user_map/account/login.html') - self.assertContains( - response, 'Please enter a correct email and password') + self.assertTemplateUsed(response, 'user_map/user_add_update.html') - def test_login_valid(self): - """Test login with valid user.""" - # Create a user first with is_confirmed = True - UserFactory.create( - email='test@mail.com', password='test', is_confirmed=True) + def test_add_user_page_with_mapped_user(self): + """Test accessing 'add user page' with mapped user. - response = self.client.post( - reverse('user_map:login'), - { - 'email': 'test@mail.com', - 'password': 'test' - } - ) + Should redirect to index page. + """ + # login with mapped user + self.assertTrue( + self.client.login( + username=self.mapped_username, + password=self.mapped_password)) + response = self.client.get(reverse('user_map:add')) self.assertRedirects( response, reverse('user_map:index'), 302, 200) - def test_logout(self): - """Test logout view.""" - response = self.client.post(reverse('user_map:logout')) + def test_add_user_success(self): + """Test adding user map using post.""" + # login with unmapped user + self.assertTrue( + self.client.login( + username=self.not_mapped_username, + password=self.not_mapped_password)) + response = self.client.post( + reverse('user_map:add'), + { + 'roles': '1', + 'location': ('{"type":"Point","coordinates":[22.5,' + '-16.63619187839765]}'), + 'image': '', + 'csrfmiddlewaretoken': u'yxeoT16pTxofWArnbgfAOudInBqAOpyq' + }) self.assertRedirects( response, reverse('user_map:index'), @@ -150,93 +121,41 @@ def test_logout(self): def test_show_update_page(self): """Test showing update user page view.""" - # Login first + # Login first with mapped user self.assertTrue( - self.client.login(email=self.email, password=self.password)) + self.client.login( + username=self.mapped_username, + password=self.mapped_password)) - response = self.client.get(reverse('user_map:update_user')) + response = self.client.get(reverse('user_map:update')) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, 'user_map/account/edit_user.html') + self.assertTemplateUsed(response, 'user_map/user_add_update.html') - def test_update_basic_information(self): - """Test update basic information.""" - # Login first + def test_update_user(self): + """Test update user.""" + # Login first with mapped user self.assertTrue( - self.client.login(email=self.email, password=self.password)) + self.client.login( + username=self.mapped_username, + password=self.mapped_password)) form_content = dict( { - 'name': 'UpdatedName', - 'email': self.email, - 'website': 'http://updated-site.com', - 'role': '1', - 'location': ('{"type":"Point","coordinates":[22.5, ' + 'roles': '1', + 'location': ('{"type":"Point","coordinates":[22.5,' '-16.63619187839765]}'), - 'email_updates': 'on', - 'change_basic_info': 'Submit' + # 'image': 'john/doe/cool.png', + 'csrfmiddlewaretoken': u'yxeoT16pTxofWArnbgfAOudInBqAOpyq' } ) response = self.client.post( - reverse('user_map:update_user'), form_content) + reverse('user_map:update'), form_content) self.assertRedirects( response, - reverse('user_map:update_user') + '#basic-information', - 302, - 200) - user = UserFactory(email=self.email) - self.assertEqual(user.name, form_content['name']) - - def test_change_password(self): - """Test change password.""" - # Login first - self.assertTrue( - self.client.login(email=self.email, password=self.password)) - - new_password = 'UpdatedPassword' - form_content = dict( - { - 'old_password': self.password, - 'new_password1': new_password, - 'new_password2': new_password, - 'change_password': 'Submit' - } - ) - response = self.client.post( - reverse('user_map:update_user'), form_content) - self.assertRedirects( - response, - reverse('user_map:update_user') + '#security', + reverse('user_map:index'), 302, 200) - - # Logout - self.client.logout() - - # Login with old password will fail - self.assertFalse( - self.client.login(email=self.email, password=self.password)) - - # Login with new password - self.assertTrue( - self.client.login(email=self.email, password=new_password)) - - def test_delete_user(self): - """Test delete_user view.""" - # Login first - self.assertTrue( - self.client.login(email=self.email, password=self.password)) - - response = self.client.post(reverse('user_map:delete_user')) - - self.assertTemplateUsed(response, 'user_map/information.html') - self.assertContains( - response, 'You have deleted your account') - - def test_download(self): - """Test download view.""" - response = self.client.post(reverse('user_map:download')) - self.assertEqual(response['Content-Type'], 'text/csv') - self.assertEqual( - response['Content-Disposition'], - 'attachment; filename="users.csv"') - self.assertContains(response, self.user.name) + user_map = UserMap.objects.get(user=self.mapped_user) + new_point = Point(22.5, -16.63619187839765) + self.assertAlmostEqual(user_map.location.x, new_point.x) + self.assertAlmostEqual(user_map.location.y, new_point.y) From 1599bc03b0a1cd202836479fba2406457a17f118 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Thu, 25 Feb 2016 22:29:17 +0100 Subject: [PATCH 14/36] Add login url for testing. --- user_map/tests/urls.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/user_map/tests/urls.py b/user_map/tests/urls.py index ed94ce2..427b6e0 100644 --- a/user_map/tests/urls.py +++ b/user_map/tests/urls.py @@ -2,5 +2,10 @@ urlpatterns = patterns( '', - url(r'^user-map/', include('user_map.urls', namespace='user_map')) + url(r'^user-map/', include('user_map.urls', namespace='user_map')), + url(r'^login/$', + 'django.contrib.auth.views.login', + {'template_name': 'admin/login.html'}, + name='my_login', + ), ) From 6e1c8f90f6ce60770883344d23584efc949f15cd Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Thu, 25 Feb 2016 22:56:01 +0100 Subject: [PATCH 15/36] Add admin site to get the namespace for login view template for testing. --- user_map/tests/urls.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/user_map/tests/urls.py b/user_map/tests/urls.py index 427b6e0..6881c67 100644 --- a/user_map/tests/urls.py +++ b/user_map/tests/urls.py @@ -1,4 +1,6 @@ +# coding=utf-8 from django.conf.urls import patterns, include, url +from django.contrib import admin urlpatterns = patterns( '', @@ -6,6 +8,6 @@ url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'admin/login.html'}, - name='my_login', - ), + name='my_login'), + url(r'^admin/', include(admin.site.urls)), ) From ea681a6d49002b058743193e97afe196ac743b2a Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Thu, 25 Feb 2016 23:20:36 +0100 Subject: [PATCH 16/36] Fix one failing test: Don't use hardcoded url --- user_map/tests/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_map/tests/test_views.py b/user_map/tests/test_views.py index 0a20928..26d9784 100644 --- a/user_map/tests/test_views.py +++ b/user_map/tests/test_views.py @@ -65,7 +65,7 @@ def test_add_user_page_without_login(self): response = self.client.get(reverse('user_map:add')) self.assertRedirects( response, - reverse(LOGIN_VIEW) + '?next=/add', + reverse(LOGIN_VIEW) + '?next=' + reverse('user_map:add'), 302, 200) From 1c40c3ccb1bf86bad94813a8d80415334e1eb268 Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Fri, 26 Feb 2016 08:09:27 +0100 Subject: [PATCH 17/36] Fix PEP8 --- user_map/management/commands/update_roles.py | 1 - user_map/tests/test_settings.py | 8 ++++---- user_map/utilities/decorators.py | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/user_map/management/commands/update_roles.py b/user_map/management/commands/update_roles.py index 99ba119..8294ab0 100644 --- a/user_map/management/commands/update_roles.py +++ b/user_map/management/commands/update_roles.py @@ -52,5 +52,4 @@ def handle(self, *args, **options): ) ) - self.stdout.write('> The operation update_role is run successfully.') diff --git a/user_map/tests/test_settings.py b/user_map/tests/test_settings.py index 5ff1299..e0e54cd 100644 --- a/user_map/tests/test_settings.py +++ b/user_map/tests/test_settings.py @@ -1,5 +1,6 @@ # coding=utf-8 import os + local_path = lambda path: os.path.join(os.path.dirname(__file__), path) SITE_ID = 1 @@ -71,14 +72,13 @@ SECRET_KEY = 'django-user-map' - USER_MAP = { 'project_name': 'Test Project', 'favicon_file': '', 'login_view': 'django.contrib.auth.views.login', 'marker': { 'icon': 'user_map/img/user-icon.png', - 'shadow': 'user_map/img/shadow-icon.png', # or 'shadow': None + 'shadow': 'user_map/img/shadow-icon.png', # or 'shadow': None }, 'leaflet_config': { 'TILES': [ @@ -87,8 +87,8 @@ 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', # The tile URL ('© OpenStreetMap and contributors, under an ' - 'OpenStreetMap and contributors, under ' + 'an open license') # The attribution )] }, diff --git a/user_map/utilities/decorators.py b/user_map/utilities/decorators.py index cc441f1..a1c9956 100644 --- a/user_map/utilities/decorators.py +++ b/user_map/utilities/decorators.py @@ -21,4 +21,3 @@ def login_forbidden(function=None, redirect_to='user_map:index'): if function: return actual_decorator(function) return actual_decorator - From 030362ce0d0300681e7d50b6426028c562d58fee Mon Sep 17 00:00:00 2001 From: Akbar Gumbira Date: Sat, 27 Feb 2016 23:36:40 +0100 Subject: [PATCH 18/36] Add custom filter control. --- user_map/static/user_map/css/user-map.css | 10 +++- user_map/static/user_map/js/user-map.js | 54 ++++++++++++++++++- user_map/templates/user_map/filter_menu.html | 8 +++ user_map/templates/user_map/index.html | 21 ++++++-- .../templates/user_map/user_menu_button.html | 2 +- user_map/views.py | 15 +++--- 6 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 user_map/templates/user_map/filter_menu.html diff --git a/user_map/static/user_map/css/user-map.css b/user_map/static/user_map/css/user-map.css index 80e33ae..6ae753d 100644 --- a/user_map/static/user_map/css/user-map.css +++ b/user_map/static/user_map/css/user-map.css @@ -10,7 +10,7 @@ html, body, #map { } #map .leaflet-top { - padding-top: 50px; + padding-top: 10px; } .transparent { @@ -71,6 +71,14 @@ html, body, #map { border-radius: 5px; } +.filter-menu { + padding: 10px 20px 10px 10px ; + background: white; + background: rgba(255,255,255,0.8); + box-shadow: 0 0 15px rgba(0,0,0,0.2); + border-radius: 5px; +} + .info h4 { margin: 4px; color: #777; diff --git a/user_map/static/user_map/js/user-map.js b/user_map/static/user_map/js/user-map.js index 02076bd..719aec2 100644 --- a/user_map/static/user_map/js/user-map.js +++ b/user_map/static/user_map/js/user-map.js @@ -32,7 +32,32 @@ function addUsers(url, users_layer, icon) { pointToLayer: function(feature, latlng) { return L.marker(latlng, {icon: icon}) } - }).addTo(users_layer) + }).addTo(users_layer); + + filterUsers(); + } + }); +} + +/** + * Filter users based on the filter control + */ +function filterUsers() { + // Clear previous displayed_users + displayed_users.clearLayers(); + + // Get all selected roles + var checked_roles = []; + $("input:checkbox[class=role-filter]:checked").each(function () { + checked_roles.push($(this).val()); + }); + + users_layer.eachLayer(function(layer) { + for (var i = 0; i < checked_roles.length; i++) { + if (layer.feature.properties.roles.indexOf(parseInt(checked_roles[i])) !== -1) { + displayed_users.addLayer(layer); + break; + } } }); } @@ -189,6 +214,33 @@ function createLegendControl(){ return control; } +/** + * Create filter control instance on the top right of the map. + * + * @returns {object} control + */ +function createFilterControl() { + var control; + control = L.Control.extend({ + options: { + position: 'topright' + }, + onAdd: function () { + var filter_container = L.DomUtil.create('div', 'filter-menu filter'); + filter_container.innerHTML += $("#role-filter").html(); + + //Prevent firing drag and onClickMap event when clicking this control + var stop = L.DomEvent.stopPropagation; + L.DomEvent + .on(filter_container, 'mousedown', stop) + .on(filter_container, 'dblclick', stop); + return filter_container; + } + }); + return control; +} + + /** * Open an information modal. There is only one modal to use for showing information. * This function should be used if there is no other specific behaviour about the modal. diff --git a/user_map/templates/user_map/filter_menu.html b/user_map/templates/user_map/filter_menu.html new file mode 100644 index 0000000..c9b1909 --- /dev/null +++ b/user_map/templates/user_map/filter_menu.html @@ -0,0 +1,8 @@ + diff --git a/user_map/templates/user_map/index.html b/user_map/templates/user_map/index.html index 9c98d0c..421128b 100644 --- a/user_map/templates/user_map/index.html +++ b/user_map/templates/user_map/index.html @@ -27,6 +27,7 @@ {{ user_menu_button | safe }} {{ information_modal | safe }} {{ data_privacy_content | safe }} + {{ filter_menu | safe }} {% if messages %}