Add new user setup workflow to walk the user through confirming their profile information, choosing categories, finding teams and attending events. Fixes #23
This commit is contained in:
parent
8d9bfa0c8d
commit
a76076e58a
20 changed files with 488 additions and 31 deletions
|
@ -5,7 +5,8 @@ from .models import Account, Badge, BadgeGrant, EmailConfirmation
|
|||
|
||||
# Register your models here.
|
||||
class AccountAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'acctname', 'email', 'is_email_confirmed')
|
||||
list_display = ('user', 'acctname', 'email', 'is_email_confirmed', 'has_completed_setup')
|
||||
list_filter = ('is_email_confirmed', 'has_completed_setup')
|
||||
def email(self, obj):
|
||||
return obj.user.email
|
||||
email.short_description = 'Email'
|
||||
|
|
27
accounts/decorators.py
Normal file
27
accounts/decorators.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
from functools import wraps
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
|
||||
from django.shortcuts import render, redirect, resolve_url
|
||||
from django.conf import settings
|
||||
|
||||
from .models import Account
|
||||
|
||||
def setup_wanted(view_func, setup_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||
"""
|
||||
Decorator for views that checks that the user has completed the setup
|
||||
process, redirecting to settings.SETUP_URL if required
|
||||
"""
|
||||
@wraps(view_func)
|
||||
def wrap(request, *args, **kwargs):
|
||||
if not request.user.is_authenticated or request.user.account.has_completed_setup:
|
||||
return view_func(request, *args, **kwargs)
|
||||
else:
|
||||
resolved_setup_url = resolve_url(setup_url or settings.SETUP_URL)
|
||||
path = request.get_full_path()
|
||||
return redirect_to_login(
|
||||
path, resolved_setup_url, redirect_field_name)
|
||||
return wrap
|
||||
|
||||
setup_required = login_required(setup_wanted)
|
23
accounts/migrations/0003_auto_20180304_1649.py
Normal file
23
accounts/migrations/0003_auto_20180304_1649.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.0 on 2018-03-04 16:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0002_auto_20180226_1532'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='has_completed_setup',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='setup_completed_date',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
|
@ -16,11 +16,19 @@ class Account(models.Model):
|
|||
acctname = models.CharField(_("Account Name"), max_length=150, blank=True)
|
||||
is_email_confirmed = models.BooleanField(default=False)
|
||||
|
||||
has_completed_setup = models.BooleanField(default=False)
|
||||
setup_completed_date = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
badges = models.ManyToManyField('Badge', through='BadgeGrant')
|
||||
|
||||
class Meta:
|
||||
ordering = ('user__username',)
|
||||
|
||||
def setup_complete(self):
|
||||
self.has_completed_setup = True
|
||||
self.setup_completed_date = datetime.datetime.now()
|
||||
self.save()
|
||||
|
||||
def new_confirmation_request(self):
|
||||
valid_for = getattr(settings, 'EMAIL_CONFIRMAION_EXPIRATION_DAYS', 5)
|
||||
confirmation_key=get_random_string(length=32)
|
||||
|
|
|
@ -59,8 +59,15 @@ class EventAdmin(admin.ModelAdmin):
|
|||
attendee_count.short_description = 'Attendees'
|
||||
admin.site.register(Event, EventAdmin)
|
||||
|
||||
admin.site.register(Member)
|
||||
admin.site.register(Attendee)
|
||||
class MemberAdmin(admin.ModelAdmin):
|
||||
list_display = ('__str__', 'role')
|
||||
list_filter = ('role', 'team')
|
||||
admin.site.register(Member, MemberAdmin)
|
||||
|
||||
class AttendeeAdmin(admin.ModelAdmin):
|
||||
list_display = ('__str__', 'role', 'status')
|
||||
list_filter = ('role', 'status')
|
||||
admin.site.register(Attendee, AttendeeAdmin)
|
||||
|
||||
class CategoryAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'image')
|
||||
|
|
|
@ -220,6 +220,11 @@ class UserProfileForm(forms.ModelForm):
|
|||
'send_notifications': _('Send me notification emails'),
|
||||
}
|
||||
|
||||
class ConfirmProfileForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ['realname', 'tz']
|
||||
|
||||
class SendNotificationsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
|
|
23
events/migrations/0013_auto_20180304_1649.py
Normal file
23
events/migrations/0013_auto_20180304_1649.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.0 on 2018-03-04 16:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0012_auto_20180227_0358'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='categories',
|
||||
field=models.ManyToManyField(blank=True, to='events.Category'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userprofile',
|
||||
name='topics',
|
||||
field=models.ManyToManyField(blank=True, to='events.Topic'),
|
||||
),
|
||||
]
|
|
@ -14,7 +14,7 @@ class UserProfile(models.Model):
|
|||
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
realname = models.CharField(verbose_name=_("Real Name"), max_length=150, blank=True)
|
||||
tz = models.CharField(max_length=32, verbose_name=_('Timezone'), default='UTC', choices=[(tz, tz) for tz in pytz.all_timezones], blank=False, null=False, help_text=_('The most commonly used timezone for this User.'))
|
||||
tz = models.CharField(max_length=32, verbose_name=_('Timezone'), default='UTC', choices=[(tz, tz) for tz in pytz.all_timezones], blank=False, null=False)
|
||||
avatar = models.URLField(verbose_name=_("Photo Image"), max_length=150, blank=True, null=True)
|
||||
|
||||
web_url = models.URLField(verbose_name=_('Website URL'), blank=True, null=True)
|
||||
|
@ -23,6 +23,9 @@ class UserProfile(models.Model):
|
|||
|
||||
send_notifications = models.BooleanField(verbose_name=_('Send notification emails'), default=True)
|
||||
|
||||
categories = models.ManyToManyField('Category', blank=True)
|
||||
topics = models.ManyToManyField('Topic', blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ('user__username',)
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ INSTALLED_APPS = [
|
|||
|
||||
LOGIN_URL = 'login'
|
||||
LOGOUT_URL = 'logout'
|
||||
SETUP_URL = 'profile/+confirm_profile'
|
||||
LOGIN_REDIRECT_URL = 'home'
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
{% extends "get_together/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<p><img class="align-bottom" border="1" src="{{profile.avatar}}" height="64px"/></p>
|
||||
<h3>Please confirm your profile information</h3>
|
||||
|
||||
<form action="{% url 'setup-1-confirm-profile' %}" method="POST" class="form">
|
||||
{% csrf_token %}
|
||||
<p>{% include "events/profile_form.html" %}</p>
|
||||
<p><button type="submit" class="btn btn-success pl-5 pr-5">Continue</button></p>
|
||||
</form>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block javascript %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
$("#id_tz").selectmenu();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,52 @@
|
|||
{% extends "get_together/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="{% static 'css/bootstrap-album.css' %}" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<h3>Tell us what kinds of events interest you</h3>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{% url 'setup-2-pick-categories' %}" method="POST" class="form">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
{% for category in categories %}
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-4 box-shadow">
|
||||
<div class="card-banner">
|
||||
<img class="card-img-top" src="{{category.img_url}}" alt="{{category.name}}">
|
||||
<p class="card-title">{{category.name}}</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text">{{category.description}}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-outline-primary category_toggle">
|
||||
<input name="category_{{category.id}}" type="checkbox" autocomplete="off"> Add
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="row pb-5">
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<button class="btn btn-success pl-5 pr-5" type="submit">Continue</button>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
{% extends "get_together/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="{% static 'css/bootstrap-album.css' %}" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<h3>Here are some nearby teams you might want to join</h3>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{% url 'setup-3-find-teams' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
{% for team in teams %}
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-4 box-shadow">
|
||||
<div class="card-banner">
|
||||
{% if team.category %}
|
||||
<img class="card-img-top" src="{{team.category.img_url}}" alt="{{team.name}}">
|
||||
{% else %}
|
||||
<img class="card-img-top" src="{% static 'img/team_placeholder.png' %}" alt="{{team.name}}">
|
||||
{% endif %}
|
||||
<p class="card-title">{{team.name}}</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text"><strong>{{team.city}}</strong></p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-outline-primary team_toggle">
|
||||
<input name="team_{{team.id}}" type="checkbox" autocomplete="off"> Join
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="row pb-5">
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<button class="btn btn-success pl-5 pr-5" type="submit">Continue</button>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
{% extends "get_together/base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block styles %}
|
||||
<link href="{% static 'css/bootstrap-album.css' %}" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<h3>Now pick some events that you'd like to attend</h3>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{% url 'setup-4-attend-events' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
{% for event in events %}
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-4 box-shadow">
|
||||
<div class="card-banner">
|
||||
{% if event.team.category %}
|
||||
<img class="card-img-top" src="{{event.team.category.img_url}}" alt="{{event.name}}">
|
||||
{% else %}
|
||||
<img class="card-img-top" src="{% static 'img/team_placeholder.png' %}" alt="{{event.name}}">
|
||||
{% endif %}
|
||||
<p class="card-title">{{event.team.name}}</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text"><strong><a class="card-link" href="{{event.get_absolute_url}}">{{event.name}}</a></strong></p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-outline-primary team_toggle">
|
||||
<input name="event_{{event.id}}" type="checkbox" autocomplete="off"> Attend
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="row pb-5">
|
||||
<div class="col-12">
|
||||
<center>
|
||||
<button class="btn btn-success pl-5 pr-5" type="submit">Continue</button>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -35,6 +35,12 @@ urlpatterns = [
|
|||
path('api/cities/', event_views.city_list),
|
||||
path('api/find_city/', event_views.find_city),
|
||||
|
||||
path('profile/+confirm_profile', views.setup_1_confirm_profile, name='setup-1-confirm-profile'),
|
||||
path('profile/+pick_categories', views.setup_2_pick_categories, name='setup-2-pick-categories'),
|
||||
path('profile/+find_teams', views.setup_3_find_teams, name='setup-3-find-teams'),
|
||||
path('profile/+attend_events', views.setup_4_attend_events, name='setup-4-attend-events'),
|
||||
path('profile/+setup_complete', views.setup_complete, name='setup-complete'),
|
||||
|
||||
path('profile/+edit', views.edit_profile, name='edit-profile'),
|
||||
path('profile/+send_confirmation_email', views.user_send_confirmation_email, name='send-confirm-email'),
|
||||
path('profile/+confirm_email/<str:confirmation_key>', views.user_confirm_email, name='confirm-email'),
|
||||
|
|
|
@ -11,6 +11,7 @@ from events.models.profiles import Team, UserProfile, Member
|
|||
from events.models.search import Searchable
|
||||
from events.forms import SearchForm
|
||||
|
||||
from accounts.decorators import setup_wanted
|
||||
from django.conf import settings
|
||||
|
||||
import datetime
|
||||
|
@ -24,12 +25,14 @@ from .events import *
|
|||
from .places import *
|
||||
from .user import *
|
||||
from .new_user import *
|
||||
from .utils import *
|
||||
|
||||
KM_PER_DEGREE_LAT = 110.574
|
||||
KM_PER_DEGREE_LNG = 111.320 # At the equator
|
||||
DEFAULT_NEAR_DISTANCE = 100 # kilometeres
|
||||
# Create your views here.
|
||||
|
||||
@setup_wanted
|
||||
def home(request, *args, **kwards):
|
||||
context = {}
|
||||
if request.user.is_authenticated:
|
||||
|
@ -49,14 +52,7 @@ def home(request, *args, **kwards):
|
|||
else :
|
||||
context['city_search'] = False
|
||||
try:
|
||||
client_ip = get_client_ip(request)
|
||||
if client_ip == '127.0.0.1' or client_ip == 'localhost':
|
||||
if settings.DEBUG:
|
||||
client_ip = '8.8.8.8' # Try Google's server
|
||||
else:
|
||||
raise Exception("Client is localhost")
|
||||
|
||||
g = geocoder.ip(client_ip)
|
||||
g = get_geoip(request)
|
||||
if g.latlng is not None and g.latlng[0] is not None and g.latlng[1] is not None:
|
||||
ll = g.latlng
|
||||
context['geoip_lookup'] = True
|
||||
|
@ -92,11 +88,3 @@ def home(request, *args, **kwards):
|
|||
context['search_form'] = search_form
|
||||
return render(request, 'get_together/index.html', context)
|
||||
|
||||
|
||||
def get_client_ip(request):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0]
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
|
|
@ -9,21 +9,126 @@ from django.core.mail import send_mail
|
|||
from django.template.loader import get_template, render_to_string
|
||||
from django.conf import settings
|
||||
|
||||
from events.models.profiles import Team, UserProfile, Member
|
||||
from events.models.profiles import Team, UserProfile, Member, Category
|
||||
from events.models.events import Event, Place, Attendee
|
||||
from events.forms import SendNotificationsForm
|
||||
from events.forms import SendNotificationsForm, UserForm, ConfirmProfileForm
|
||||
|
||||
from .utils import get_nearby_teams
|
||||
|
||||
import datetime
|
||||
import simplejson
|
||||
|
||||
def new_user_confirm_profile(request):
|
||||
pass
|
||||
@login_required
|
||||
def setup_1_confirm_profile(request):
|
||||
user = request.user
|
||||
profile = request.user.profile
|
||||
|
||||
def new_user_find_teams(request):
|
||||
pass
|
||||
if request.method == 'GET':
|
||||
user_form = UserForm(instance=user)
|
||||
profile_form = ConfirmProfileForm(instance=profile)
|
||||
context = {
|
||||
'user': user,
|
||||
'profile': profile,
|
||||
'user_form': user_form,
|
||||
'profile_form': profile_form,
|
||||
}
|
||||
return render(request, 'get_together/new_user/setup_1_confirm_profile.html', context)
|
||||
elif request.method == 'POST':
|
||||
user_form = UserForm(request.POST, instance=user)
|
||||
profile_form = ConfirmProfileForm(request.POST, instance=profile)
|
||||
if user_form.is_valid() and profile_form.is_valid():
|
||||
saved_user = user_form.save()
|
||||
profile_form.save()
|
||||
if saved_user.email is not None and saved_user.email != '' and not saved_user.account.is_email_confirmed:
|
||||
# Call the view to trigger sending a confirmation email, but ignore it's response
|
||||
user_send_confirmation_email(request)
|
||||
return redirect('setup-2-pick-categories')
|
||||
else:
|
||||
return redirect('home')
|
||||
|
||||
def new_user_find_events(request):
|
||||
pass
|
||||
|
||||
@login_required
|
||||
def setup_2_pick_categories(request):
|
||||
user = request.user
|
||||
profile = request.user.profile
|
||||
|
||||
if request.method == 'GET':
|
||||
categories = Category.objects.all()
|
||||
context = {
|
||||
'user': user,
|
||||
'profile': profile,
|
||||
'categories': categories,
|
||||
}
|
||||
return render(request, 'get_together/new_user/setup_2_pick_categories.html', context)
|
||||
elif request.method == 'POST':
|
||||
for entry in request.POST:
|
||||
if entry.startswith('category_'):
|
||||
category_id = entry.split('_')[1]
|
||||
try:
|
||||
profile.categories.add(category_id)
|
||||
except:
|
||||
pass
|
||||
return redirect('setup-3-find-teams')
|
||||
else:
|
||||
return redirect('home')
|
||||
|
||||
@login_required
|
||||
def setup_3_find_teams(request):
|
||||
user = request.user
|
||||
profile = request.user.profile
|
||||
if request.method == 'GET':
|
||||
teams = get_nearby_teams(request)
|
||||
if (teams.count() < 1):
|
||||
return redirect('setup-complete')
|
||||
context = {
|
||||
'user': user,
|
||||
'profile': profile,
|
||||
'teams': teams,
|
||||
}
|
||||
return render(request, 'get_together/new_user/setup_3_find_teams.html', context)
|
||||
elif request.method == 'POST':
|
||||
for entry in request.POST:
|
||||
if entry.startswith('team_'):
|
||||
team_id = entry.split('_')[1]
|
||||
try:
|
||||
Member.objects.get_or_create(team_id=team_id, user=profile, defaults={'role': Member.NORMAL})
|
||||
except Member.MultipleObjectsReturned:
|
||||
pass
|
||||
return redirect('setup-4-attend-events')
|
||||
else:
|
||||
return redirect('home')
|
||||
|
||||
@login_required
|
||||
def setup_4_attend_events(request):
|
||||
user = request.user
|
||||
profile = request.user.profile
|
||||
if request.method == 'GET':
|
||||
events = Event.objects.filter(team__in=profile.memberships.all(), end_time__gte=datetime.datetime.now())
|
||||
if (events.count() < 1):
|
||||
return redirect('setup-complete')
|
||||
context = {
|
||||
'user': user,
|
||||
'profile': profile,
|
||||
'events': events,
|
||||
}
|
||||
return render(request, 'get_together/new_user/setup_4_attend_events.html', context)
|
||||
elif request.method == 'POST':
|
||||
for entry in request.POST:
|
||||
if entry.startswith('event_'):
|
||||
event_id = entry.split('_')[1]
|
||||
try:
|
||||
Attendee.objects.get_or_create(event_id=event_id, user=profile, defaults={'role': Attendee.NORMAL, 'status': Attendee.YES})
|
||||
except Attendee.MultipleObjectsReturned:
|
||||
pass
|
||||
return redirect('setup-complete')
|
||||
else:
|
||||
return redirect('home')
|
||||
|
||||
@login_required
|
||||
def setup_complete(request):
|
||||
messages.add_message(request, messages.SUCCESS, message=_('Your setup is complete, welcome to GetTogether!'))
|
||||
request.user.account.setup_complete()
|
||||
return redirect('home')
|
||||
|
||||
# These views are for confirming a user's email address before sending them mail
|
||||
@login_required
|
||||
|
@ -48,7 +153,7 @@ def user_send_confirmation_email(request):
|
|||
recipient_list=email_recipients,
|
||||
html_message=email_body_html
|
||||
)
|
||||
return render(request, 'get_together/users/sent_email_confirmation.html', context)
|
||||
return render(request, 'get_together/new_user/sent_email_confirmation.html', context)
|
||||
|
||||
@login_required
|
||||
def user_confirm_email(request, confirmation_key):
|
||||
|
@ -56,7 +161,7 @@ def user_confirm_email(request, confirmation_key):
|
|||
messages.add_message(request, messages.SUCCESS, message=_('Your email address has been confirmed.'))
|
||||
return redirect('confirm-notifications')
|
||||
else:
|
||||
return render(request, 'get_together/users/bad_email_confirmation.html')
|
||||
return render(request, 'get_together/new_user/bad_email_confirmation.html')
|
||||
|
||||
@login_required
|
||||
def user_confirm_notifications(request):
|
||||
|
@ -65,7 +170,7 @@ def user_confirm_notifications(request):
|
|||
context = {
|
||||
'notifications_form': form
|
||||
}
|
||||
return render(request, 'get_together/users/confirm_notifications.html', context)
|
||||
return render(request, 'get_together/new_user/confirm_notifications.html', context)
|
||||
elif request.method == 'POST':
|
||||
form = SendNotificationsForm(request.POST, instance=request.user.profile)
|
||||
if form.is_valid():
|
||||
|
|
68
get_together/views/utils.py
Normal file
68
get_together/views/utils.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import logout as logout_user
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
|
||||
from events.models.locale import City
|
||||
from events.models.events import Event, Place, Attendee
|
||||
from events.models.profiles import Team, UserProfile, Member
|
||||
from events.models.search import Searchable
|
||||
from events.forms import SearchForm
|
||||
|
||||
from accounts.decorators import setup_wanted
|
||||
from django.conf import settings
|
||||
|
||||
import datetime
|
||||
import simplejson
|
||||
import geocoder
|
||||
import math
|
||||
import traceback
|
||||
|
||||
from .teams import *
|
||||
from .events import *
|
||||
from .places import *
|
||||
from .user import *
|
||||
from .new_user import *
|
||||
|
||||
KM_PER_DEGREE_LAT = 110.574
|
||||
KM_PER_DEGREE_LNG = 111.320 # At the equator
|
||||
DEFAULT_NEAR_DISTANCE = 100 # kilometeres
|
||||
|
||||
def get_geoip(request):
|
||||
client_ip = get_client_ip(request)
|
||||
if client_ip == '127.0.0.1' or client_ip == 'localhost':
|
||||
if settings.DEBUG:
|
||||
client_ip = '8.8.8.8' # Try Google's server
|
||||
print("Client is localhost, using 8.8.8.8 for geoip instead")
|
||||
else:
|
||||
raise Exception("Client is localhost")
|
||||
|
||||
g = geocoder.ip(client_ip)
|
||||
return g
|
||||
|
||||
def get_nearby_teams(request, near_distance=DEFAULT_NEAR_DISTANCE):
|
||||
g = get_geoip(request)
|
||||
if g.latlng is None or g.latlng[0] is None or g.latlng[1] is None:
|
||||
print("Could not identify latlng from geoip")
|
||||
return Team.objects.none()
|
||||
try:
|
||||
minlat = g.latlng[0]-(near_distance/KM_PER_DEGREE_LAT)
|
||||
maxlat = g.latlng[0]+(near_distance/KM_PER_DEGREE_LAT)
|
||||
minlng = g.latlng[1]-(near_distance/(KM_PER_DEGREE_LNG*math.cos(math.radians(g.latlng[0]))))
|
||||
maxlng = g.latlng[1]+(near_distance/(KM_PER_DEGREE_LNG*math.cos(math.radians(g.latlng[0]))))
|
||||
|
||||
near_teams = Team.objects.filter(city__latitude__gte=minlat, city__latitude__lte=maxlat, city__longitude__gte=minlng, city__longitude__lte=maxlng)
|
||||
return near_teams
|
||||
except Exception as e:
|
||||
print("Error looking for local teams: ", e)
|
||||
return Team.objects.none()
|
||||
|
||||
def get_client_ip(request):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0]
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
Loading…
Reference in a new issue