diff --git a/blechreiz/settings.py b/blechreiz/settings.py index 877019c..e072f95 100644 --- a/blechreiz/settings.py +++ b/blechreiz/settings.py @@ -78,8 +78,8 @@ STATIC_ROOT = PROJECT_PATH + '/static_collection' # Example: "http://example.com/static/", "http://static.example.com/" STATIC_URL = '/static/' -LOGIN_URL="/login" -PUBLIC_URLS=( "^login/?$", "^login/usernames/?$") +LOGIN_URL="/musicians/login" +PUBLIC_URLS=( "^musicians/login/?$", "^musicians/login/usernames/?$") # Additional locations of static files STATICFILES_DIRS = ( diff --git a/eventplanner/models.py b/eventplanner/models.py index 8bd01aa..fbab014 100644 --- a/eventplanner/models.py +++ b/eventplanner/models.py @@ -9,6 +9,11 @@ from datetime import datetime from location_field.models import PlainLocationField +class NoNextEventException( Exception ): + def __str__(self): + return ("No event scheduled for the future") + + class Event ( models.Model ): EVENT_TYPES = ( @@ -64,11 +69,50 @@ class Event ( models.Model ): @property def displaydatetime(self): if not self.displaytime == None: - return datetime.combine( self.date, self.displaytime() ) + return datetime.combine( self.date, self.displaytime ) else: - return datetime.combine( self.date, datetime.min.time()) + return datetime.combine( self.date, datetime.min.time() ) + @staticmethod + def getNextEvent( eventType = "", includePreviousFromToday = True ): + """Return the next event, of the given type. If type is the empty string the next event is returned + regardless of its type. + if includePreviousFromToday the nextEvent returned could also have been today with a startime < now """ + if includePreviousFromToday: + if eventType == "": + nextEvents = Event.objects.filter( date__gte = datetime.now() ).order_by('date')[:1] + else: + nextEvents = Event.objects.filter( date__gte = datetime.now(), type = eventType ).order_by('date')[:1] + + if len( nextEvents ) == 0: + raise NoNextEventException() + + return nextEvents[0] + else: + maximalNumberOfEventsOnSameDay = 4 + nextEvents = [] + if eventType =="": + nextEvents = Event.objects.filter( date__gte = datetime.now() ).order_by('date')[:maximalNumberOfEventsOnSameDay] + else: + nextEvents = Event.objects.filter( date__gte = datetime.now(), type = eventType ).order_by('date')[:maximalNumberOfEventsOnSameDay] + + if len(nextEvents) == 0: + raise NoNextEventException() + + + i = 0 + nextEvent = nextEvents[0] + # nextEvent is not necessarily events[0] since events[0] may have been previously today + while nextEvent.displaydatetime < datetime.now(): + if len(nextEvents ) <= i: + raise NoNextEventException() + else: + i += 1 + nextEvent = nextEvents[i] + + return nextEvent + class EventParticipation( models.Model ): OPTIONS = ( ('?' , _('?' )), diff --git a/eventplanner/snippets.py b/eventplanner/snippets.py new file mode 100644 index 0000000..8495b9b --- /dev/null +++ b/eventplanner/snippets.py @@ -0,0 +1,55 @@ + +from datetime import datetime +from models import Event, EventParticipation, NoNextEventException +from musicians.models import Musician + + + +def addEventCountdownForNextEventToContext( context, username, eventType = "" ): + """Returns an object that has to be added to the render context on the page where the countdown + should be displayed . The username is required to also supply participation information.""" + + try: + nextEvent = Event.getNextEvent( eventType, False ) + except NoNextEventException: + return + + countdown = dict() + + if EventParticipation.isMember( username ): + part = EventParticipation.objects.filter( user = username ).filter( event = nextEvent ) + countdown['participation'] = part[0].status + + eventTime = nextEvent.displaydatetime + countdown['event'] = nextEvent + countdown['epoch'] = int( (eventTime - datetime.now() ).total_seconds() * 1000 ) + + context["countdown"] = countdown + + +def addEventRouteForNextEventToContext( context, username, eventType = ""): + """Returns an object that has to be added to the render context on the page where the route + should be displayed . The starting address of the route will be the home of the specified user""" + + try: + nextEvent = Event.getNextEvent( eventType, True ) + except NoNextEventException: + return + + routeInfo = dict() + + routeInfo['event'] = nextEvent + + musician = Musician.objects.get( user = username ); + routeInfo['origin'] = musician.street + ", " + str( musician.zip_code ) + " " + musician.city + + if nextEvent.map_location: + # map_location has format "lat,longitute,zoomlevel" + routeInfo['destination'] = ",".join( nextEvent.map_location.split(",")[:2] ) + else: + routeInfo['destination'] = nextEvent.location + + + context["route"] = routeInfo + + diff --git a/website/templates/website/event_countdown.html b/eventplanner/templates/eventplanner/countdown.inc.html similarity index 100% rename from website/templates/website/event_countdown.html rename to eventplanner/templates/eventplanner/countdown.inc.html diff --git a/website/templates/website/concert_route.html b/eventplanner/templates/eventplanner/routeToEventMap.inc.html similarity index 85% rename from website/templates/website/concert_route.html rename to eventplanner/templates/eventplanner/routeToEventMap.inc.html index 15ddad9..63ab113 100644 --- a/website/templates/website/concert_route.html +++ b/eventplanner/templates/eventplanner/routeToEventMap.inc.html @@ -3,12 +3,10 @@ Context: Coordinates or textual adresses: - {{routeInfo.origin}} - {{routeInfo.destination}} - + {{route.origin}} + {{route.destination}} Event object: - {{nextConcert}} - + {{route.event}} {% endcomment %} @@ -16,9 +14,8 @@ {% load sekizai_tags staticfiles %} -{% if hasNextConcertInfo %} - +{% if route %} {% addtoblock "css" strip %}{% endaddtoblock %} {% addtoblock "js" strip %}{% endaddtoblock %} @@ -86,10 +83,10 @@ google.maps.event.addDomListener(controlUI, 'click', function() { - {% if not nextConcert.map_location %} + {% if not route.event.map_location %} geocoder = new google.maps.Geocoder(); geocoder.region = "de"; - geocoder.geocode( {"address": "{{ nextConcert.location }}" }, function(results, status) { + geocoder.geocode( {"address": "{{ route.event.location }}" }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { map.setMapTypeId( google.maps.MapTypeId.HYBRID ); map.setZoom( 15 ); @@ -98,7 +95,7 @@ } }); {% else %} - var loc = new google.maps.LatLng( {{ nextConcert.map_location }} ); + var loc = new google.maps.LatLng( {{ route.event.map_location }} ); map.setMapTypeId( google.maps.MapTypeId.HYBRID ); map.setZoom( 20 ); @@ -127,8 +124,8 @@ directionsDisplay.setMap( map ); var request = { - origin: "{{routeInfo.origin}}", - destination: "{{routeInfo.destination}}", + origin: "{{route.origin}}", + destination: "{{route.destination}}", travelMode: google.maps.DirectionsTravelMode.DRIVING } @@ -180,8 +177,8 @@ - - - - {% if nextConcert.meeting_time %} {% endif %} + + + + {% if route.event.meeting_time %} {% endif %}
Ort: {{nextConcert.location}}
Datum: {{nextConcert.date | date:"D, d.m.y" }}
Uhrzeit: {{nextConcert.time | time:"H:i" }} Uhr
Treffen um: {{ nextConcert.meeting_time | time:"H:i" }} Uhr
Ort: {{route.event.location}}
Datum: {{route.event.date | date:"D, d.m.y" }}
Uhrzeit: {{route.event.time | time:"H:i" }} Uhr
Treffen um: {{route.event.meeting_time | time:"H:i" }} Uhr
Fahrzeit:
Strecke:
diff --git a/website/templates/website/change_password.html b/musicians/templates/musicians/change_password.html similarity index 100% rename from website/templates/website/change_password.html rename to musicians/templates/musicians/change_password.html diff --git a/website/templates/website/login.html b/musicians/templates/musicians/login.html similarity index 98% rename from website/templates/website/login.html rename to musicians/templates/musicians/login.html index fb8d5ac..0141060 100644 --- a/website/templates/website/login.html +++ b/musicians/templates/musicians/login.html @@ -60,7 +60,7 @@ }); - $.get( "/login/usernames", function( data ) { + $.get( "/musicians/login/usernames", function( data ) { $("#username").autocomplete( { source: data } ); }); }); diff --git a/musicians/urls.py b/musicians/urls.py index 9d6c88c..45ca4bf 100644 --- a/musicians/urls.py +++ b/musicians/urls.py @@ -6,6 +6,9 @@ import musicians.views urlpatterns = patterns('', url(r'^$', musicians.views.addressbook ), url(r'^profile$', musicians.views.own_profile ), - #url(r'^(?P[\w]+)$$', musicians.views.user_edit ), + url(r'^changePassword/$', musicians.views.change_password ), + url(r'^login/$', musicians.views.login_view), + url(r'^login/usernames$', musicians.views.userlistForAutocompletion), + url(r'^logout/$', musicians.views.logout_view), ) diff --git a/musicians/views.py b/musicians/views.py index fccc177..f7268ec 100644 --- a/musicians/views.py +++ b/musicians/views.py @@ -79,9 +79,76 @@ def addressbook( request ): return render( request, 'musicians/addressbook.html', context ) +############################################################################################################ +######################### User Management Views ############################################################ +############################################################################################################ + +from django.contrib.auth.views import password_change +from django.contrib.auth import authenticate, login, logout +from django.shortcuts import redirect +from django.http import HttpResponse +from django.utils import simplejson +from django.contrib.auth.models import User +from datetime import timedelta + + +def change_password( request ): + return password_change(request, "musicians/change_password.html", post_change_redirect= "/" ) + +def logout_view(request): + logout( request ) + return redirect( login_view ) + + +def userlistForAutocompletion(request): + result = [ u.username for u in User.objects.all() ] + return HttpResponse( simplejson.dumps(result), mimetype='application/json' ) + + +def login_view( request ): + if request.method == 'POST': # If the form has been submitted... + raiseFirstLetter = lambda s: s[:1].upper() + s[1:] if s else '' + username = raiseFirstLetter( request.POST['username'] ) + password = request.POST['password'] + user = authenticate( username=username, password=password ) + result = dict() + result['err'] = "" + if user is not None: + if user.is_active: + if not request.POST.get('remember', None): + # Expire in one year + request.session.set_expiry( timedelta( weeks=52 ) ) + else: + # Expire on browser close + request.session.set_expiry( 0 ) + + login(request, user) + result['redirect'] = "/" + print ( "Setting Redirect" ) + if 'next' in request.POST : + result['redirect'] = request.POST["next"] + print ( "Using " + request.POST["next"] ) + else: + result['err'] = "Dein Account wurde deaktiviert." + # Return a 'disabled account' error message + else: + result['err'] = "Falscher Benutzername oder falsches Kennwort." + + return HttpResponse( simplejson.dumps(result), mimetype='application/json' ) - + else: + # Check if user already logged in + if request.user.is_authenticated(): + return redirect( "/") + + if 'next' in request.GET: + nextPage = request.GET['next'] + else: + nextPage = "/" + return render( request, 'musicians/login.html', { 'next' : nextPage } ) + + diff --git a/website/templates/website/base.html b/website/templates/website/base.html index c67759b..b4a669d 100644 --- a/website/templates/website/base.html +++ b/website/templates/website/base.html @@ -48,8 +48,8 @@ {% endif %} diff --git a/website/templates/website/mainpage.html b/website/templates/website/mainpage.html index 96196e0..185c8cb 100644 --- a/website/templates/website/mainpage.html +++ b/website/templates/website/mainpage.html @@ -1,14 +1,12 @@ {% extends "website/base.html" %} - {% load sekizai_tags staticfiles %} - {% block content %} - {% include "website/event_countdown.html" %} - {% include "website/concert_route.html" %} + {% include "eventplanner/countdown.inc.html" %} + {% include "eventplanner/routeToEventMap.inc.html" %} {% endblock %} diff --git a/website/urls.py b/website/urls.py index d844ad3..93ad2a4 100644 --- a/website/urls.py +++ b/website/urls.py @@ -1,13 +1,9 @@ from django.conf.urls import patterns, url -from website.views import home_view, login_view, userlistForAutocompletion,logout_view, change_password +from website.views import home_view urlpatterns = patterns('', - url(r'$^', home_view ), - url(r'^login/$', login_view), - url(r'^login/usernames$', userlistForAutocompletion), - url(r'^logout/$', logout_view), - url(r'^changePassword/$', change_password), + url(r'$^', home_view ), ) diff --git a/website/views.py b/website/views.py index 4d091e7..c116325 100644 --- a/website/views.py +++ b/website/views.py @@ -1,20 +1,8 @@ -# Create your views here. +from django.shortcuts import render -from django.shortcuts import render, redirect -from django.contrib.auth import authenticate, login, logout - -from django.http import HttpResponse - -from django.utils import simplejson from django.contrib.auth.decorators import login_required - - -from eventplanner.models import Event, EventParticipation -from musicians.models import Musician - -from datetime import datetime -from datetime import timedelta -from django.contrib.auth.models import User +from eventplanner.snippets import addEventCountdownForNextEventToContext, addEventRouteForNextEventToContext +from eventplanner.models import EventParticipation @login_required @@ -25,117 +13,8 @@ def home_view(request): if EventParticipation.isMember( request.user ): context['hasParticipationSetForAllEvents'] = EventParticipation.hasUserSetParticipationForAllEvents( request.user) - # Countdown - countdown = dict() - events = Event.objects.filter( date__gte = datetime.now() ).order_by('date')[:3] - - if ( len(events) > 0 ): - i = 0 - nextEvent = events[0] - - while nextEvent.displaydatetime < datetime.now(): - if len(events ) <= i: - return - else: - i += 1 - nextEvent = events[i] - - if EventParticipation.isMember( request.user ): - part = EventParticipation.objects.filter( user = request.user ).filter( event = nextEvent ) - countdown['participation'] = part[0].status - - eventTime = nextEvent.displaydatetime - - countdown['event'] = nextEvent - countdown['epoch'] = int( (eventTime - datetime.now() ).total_seconds() * 1000 ) - - context['countdown'] = countdown - - - # Route to concert - nextConcerts = Event.objects.filter( date__gte = datetime.now(), type = 'Conc' ).order_by('date')[:1] - if len( nextConcerts) > 0 : - nextConcert = nextConcerts[0] - - routeInfo = dict() - - musician = Musician.objects.get( user = request.user ); - routeInfo['origin'] = musician.street + ", " + str( musician.zip_code ) + " " + musician.city - - if nextConcert.map_location: - # map_location has format "lat,longitute,zoomlevel" - routeInfo['destination'] = ",".join( nextConcert.map_location.split(",")[:2] ) - else: - routeInfo['destination'] = nextConcert.location - - context['routeInfo'] = routeInfo - context['nextConcert'] = nextConcert - context['hasNextConcertInfo'] = True - else: - context['hasNextConcertInfo'] = False - + addEventCountdownForNextEventToContext( context, request.user ); + addEventRouteForNextEventToContext(context, request.user, 'Conc' ) return render( request, 'website/mainpage.html', context ) - -def logout_view(request): - logout( request ) - return redirect( login_view ) - - -def userlistForAutocompletion(request): - result = [ u.username for u in User.objects.all() ] - return HttpResponse( simplejson.dumps(result), mimetype='application/json' ) - - -def login_view( request ): - if request.method == 'POST': # If the form has been submitted... - raiseFirstLetter = lambda s: s[:1].upper() + s[1:] if s else '' - username = raiseFirstLetter( request.POST['username'] ) - password = request.POST['password'] - user = authenticate( username=username, password=password ) - result = dict() - result['err'] = "" - if user is not None: - if user.is_active: - if not request.POST.get('remember', None): - # Expire in one year - request.session.set_expiry( timedelta( weeks=52 ) ) - else: - # Expire on browser close - request.session.set_expiry( 0 ) - - login(request, user) - result['redirect'] = "/" - print ( "Setting Redirect" ) - if 'next' in request.POST : - result['redirect'] = request.POST["next"] - print ( "Using " + request.POST["next"] ) - else: - result['err'] = "Dein Account wurde deaktiviert." - # Return a 'disabled account' error message - else: - result['err'] = "Falscher Benutzername oder falsches Kennwort." - - return HttpResponse( simplejson.dumps(result), mimetype='application/json' ) - - else: - # Check if user already logged in - if request.user.is_authenticated(): - return redirect( "/") - - if 'next' in request.GET: - nextPage = request.GET['next'] - else: - nextPage = "/" - return render( request, 'website/login.html', { 'next' : nextPage } ) - - - -import django.contrib.auth.views -from django.core.urlresolvers import reverse - -def change_password( request ): - template_name = "website/change_password.html" - return django.contrib.auth.views.password_change(request, template_name, post_change_redirect= "/" ) -