Eventplanner

- cleaned up urls
- permissions
This commit is contained in:
Martin Bauer 2013-06-30 16:55:00 +02:00
parent 01a7f5c16f
commit e2d98d9962
13 changed files with 271 additions and 101 deletions

Binary file not shown.

View File

@ -153,7 +153,7 @@ INSTALLED_APPS = (
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
'PAGINATE_BY': 10
}

View File

@ -16,15 +16,9 @@ from django.conf.urls.static import static
admin.autodiscover()
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'blechreiz.views.home', name='home'),
# url(r'^blechreiz/', include('blechreiz.foo.urls')),
# Uncomment the admin/doc line below to enable admin documentation:
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^events/', include( eventplanner.views.urls) ),
url(r'^admin/', include(admin.site.urls) ),
url(r'^admin/doc/', include('django.contrib.admindocs.urls') ),
url(r'^musicians/$', MusicianList.as_view() ),
url(r'^musicians/(?P<username>[\w]+)$', user_edit ),
@ -32,10 +26,4 @@ urlpatterns = patterns('',
url(r'^website/$', website.views.home_view),
url(r'^login/$', website.views.login_view),
url(r'^logout/$', website.views.logout_view),
url(r'^events/$', eventplanner.views.eventplanning_view),
url(r'^eventParticipation/$', eventplanner.views.event_participation_detail ),
url(r'^eventParticipation/(\w+)/$', eventplanner.views.event_participation_detail ),
url(r'^eventParticipation/(\w+)/(\d+)$', eventplanner.views.event_participation_detail ),
url(r'^events/grid$', eventplanner.views.events_grid ),
url(r'^events/(?P<pk>\d+)$', eventplanner.views.EventUpdate.as_view() ),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-06-30 10:55+0200\n"
"POT-Creation-Date: 2013-06-30 16:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,18 +18,22 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: models.py:11
#: models.py:10
msgid "Rehearsal"
msgstr "Probe"
#: models.py:12
#: models.py:11
msgid "Concert"
msgstr "Konzert"
#: models.py:13
#: models.py:12
msgid "General Rehearsal"
msgstr "Generalprobe"
#: models.py:13
msgid "Party"
msgstr "Feier"
#: models.py:16
msgid "title"
msgstr "Titel"
@ -39,52 +43,70 @@ msgid "type"
msgstr "Typ"
#: models.py:18
msgid "date"
msgstr "Datum"
#: models.py:19
msgid "time"
msgstr "Uhrzeit"
#: models.py:20
msgid "participants"
msgstr "Teilnehmer"
#: models.py:22
msgid "description"
msgstr "Beschreibung"
#: models.py:23
msgid "location"
msgstr "Ort"
#: models.py:44
#: models.py:19
msgid "description"
msgstr "Beschreibung"
#: models.py:21
msgid "date"
msgstr "Datum"
#: models.py:22
msgid "time"
msgstr "Uhrzeit"
#: models.py:23
msgid "meeting_time"
msgstr ""
#: models.py:25
msgid "participants"
msgstr "Teilnehmer"
#: models.py:45
msgid "?"
msgstr ""
#: models.py:45
#: models.py:46
msgid "Yes"
msgstr "Ja"
#: models.py:46
#: models.py:47
msgid "No"
msgstr "Nein"
#: models.py:49
#: models.py:50
msgid "event"
msgstr "Termin"
#: models.py:50
#: models.py:51
msgid "musician"
msgstr "Musiker"
#: models.py:51
#: models.py:52
msgid "status"
msgstr "Status"
#: models.py:52
#: models.py:53
msgid "comment"
msgstr "Kommentar"
#~ msgid "Delete?"
#~ msgstr "Löschen"
#: models.py:72
msgid "Can modify participation status of other users"
msgstr ""
#: templates/custom_tabular.html:17
msgid "Delete?"
msgstr "Löschen"
#: templates/custom_tabular.html:74
#, python-format
msgid "Add another %(verbose_name)s"
msgstr ""
#: templates/custom_tabular.html:75
msgid "Remove"
msgstr ""

View File

@ -1,6 +1,5 @@
from django.db import models
from django.utils.translation import ugettext as _
from locpick.field import LocationField
from musicians.models import Musician
@ -11,18 +10,20 @@ class Event ( models.Model ):
( 'Reh', _('Rehearsal') ),
( 'Conc', _('Concert') ),
( 'GenReh', _('General Rehearsal') ),
( 'Party', _('Party') ),
)
title = models.CharField( max_length=40, verbose_name = _("title") )
type = models.CharField( max_length=6, choices=EVENT_TYPES, default='Reh', verbose_name= _("type") )
location = models.TextField( blank=False, verbose_name=_("location") )
desc = models.TextField( blank=True, verbose_name=_("description"))
date = models.DateField( verbose_name= _("date") )
time = models.TimeField( null=True, blank=True, verbose_name = _("time") )
meeting_time = models.TimeField( null=True, blank=True, verbose_name = _("meeting_time") )
participants = models.ManyToManyField( Musician, through='EventParticipation', verbose_name=_("participants") )
desc = models.TextField( blank=True, verbose_name=_("description"))
location = LocationField( verbose_name=_("location") )
def __unicode__(self):
return self.title + " ( " + self.get_type_display() + " ) "
@ -51,10 +52,25 @@ class EventParticipation( models.Model ):
status = models.CharField ( max_length=3, choices = OPTIONS, default='?', verbose_name=_("status") )
comment = models.CharField ( max_length=64, blank=True, verbose_name=_("comment") )
def get_musician_username(self):
return self.musician.user.username
@staticmethod
def get_or_create( musician , event ):
try:
result = EventParticipation.objects.get( event = event, musician = musician )
except EventParticipation.DoesNotExist:
result = EventParticipation.objects.create( event = event, musician = musician, status='?', comment = '' )
return result
class Meta:
unique_together = ("event", "musician")
permissions = (
("change_others_participation", _("Can modify participation status of other users") ),
)

View File

@ -0,0 +1,78 @@
{% load i18n admin_static admin_modify %}
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
{{ inline_admin_formset.formset.management_form }}
<fieldset class="module">
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
{{ inline_admin_formset.formset.non_form_errors }}
<table>
<thead><tr>
{% for field in inline_admin_formset.fields %}
{% if not field.widget.is_hidden %}
<th{% if forloop.first %} colspan="2"{% endif %}{% if field.required %} class="required"{% endif %}>{{ field.label|capfirst }}
{% if field.help_text %}&nbsp;<img src="{% static "admin/img/icon-unknown.gif" %}" class="help help-tooltip" width="10" height="10" alt="({{ field.help_text|striptags }})" title="{{ field.help_text|striptags }}" />{% endif %}
</th>
{% endif %}
{% endfor %}
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
</tr></thead>
<tbody>
{% for inline_admin_form in inline_admin_formset %}
{% if inline_admin_form.form.non_field_errors %}
<tr><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
{% endif %}
<tr class="form-row {% cycle "row1" "row2" %} {% if forloop.last %} empty-form{% endif %}"
id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
<td class="original">
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
</p>{% endif %}
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{{ inline_admin_form.fk_field.field }}
{% spaceless %}
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{% if field.is_hidden %} {{ field.field }} {% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endspaceless %}
</td>
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
<td{% if field.field.name %} class="field-{{ field.field.name }}"{% endif %}>
{% if field.is_readonly %}
<p>{{ field.contents|linebreaksbr }}</p>
{% else %}
{{ field.field.errors.as_ul }}
{{ field.field }}
{% endif %}
</td>
{% endfor %}
{% endfor %}
{% endfor %}
{% if inline_admin_formset.formset.can_delete %}
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
</div>
</div>
<script type="text/javascript">
(function($) {
$("#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr").tabularFormset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
adminStaticPrefix: '{% static "admin/" %}',
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
deleteText: "{% trans 'Remove' %}"
});
})(django.jQuery);
</script>

View File

@ -51,7 +51,7 @@
request = $.ajax( {
type: "PUT",
url: "/eventParticipation/" + p.data("username") + "/" + p.data("event-id"),
url: "{% url 'event_api' %}",
contentType: "application/json",
data: JSON.stringify(putObject)
});
@ -73,7 +73,7 @@
$.ajax( {
type: "PUT",
url: "/eventParticipation/" + $(this).data("username") + "/" + $(this).data("event-id"),
url: "{% url 'event_api' %}",
contentType: "application/json",
data: JSON.stringify(putObject)
});

View File

@ -29,9 +29,14 @@
{% addtoblock "js" %}
<script>
{% if not perms.eventplanner.change_others_participation %}
$(".eventButton").attr('disabled', 'disabled');
{% endif %}
$('.eventButton').tooltip()
$(".eventButton").click( function () {
$(".eventButton").click( function (e) {
e.preventDefault();
$(this).removeClass("btn-danger")
.removeClass("btn-warning")
.removeClass("btn-success");
@ -57,8 +62,10 @@
$('#saveButton').removeAttr('disabled');
});
$("#saveButton").click( function() {
$("#saveButton").click( function(e) {
e.preventDefault();
arr = [];
$('.userEventTableData').each( function() {
@ -74,7 +81,7 @@
$.ajax( {
type: "PUT",
url: "/eventParticipation/",
url: "{% url 'event_api' %}",
contentType: "application/json",
data: JSON.stringify(arr)
});
@ -146,11 +153,16 @@
</div><!--/row-->
{% if perms.eventplanner.change_others_participation %}
<div class="row">
<div class="span12">
<button id="saveButton" class="btn btn-primary" disabled="true" >Speichern</button>
</div>
</div>
{% endif %}
</form>
</div>

View File

@ -1,16 +0,0 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

View File

@ -16,15 +16,19 @@ from rest_framework import status
from django.forms.models import ModelForm
from django.conf.urls import patterns, url
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
# ---------------------------------------- API ---------------------------------------------------------
@api_view( ['GET', 'PUT'] )
def event_participation_detail( request, username = None, eventId = None ):
# TODO Permissions!
def event_api( request, username = None, eventId = None ):
try:
participationQs = EventParticipation.objects.filter( event__date__gte = datetime.date.today() )
if username:
@ -43,6 +47,11 @@ def event_participation_detail( request, username = None, eventId = None ):
elif request.method == 'PUT':
serializer = ParticipationSerializer ( participationQs, data = request.DATA, many=True )
if serializer.is_valid():
for serializedObject in serializer.object:
if serializedObject.musician.user != request.user:
if not request.user.has_perm('change_others_participation') :
return Response( status = status.HTTP_403_FORBIDDEN )
serializer.save()
return Response( serializer.data )
else:
@ -50,16 +59,17 @@ def event_participation_detail( request, username = None, eventId = None ):
# ------------------------------------ Normal Views ----------------------------------------------------
@login_required
def main_view( request ):
if request.user.has_perm( 'eventplanner.change_others_participation'):
return events_grid( request )
else:
return eventplanning( request )
def eventplanning_view( request ):
@login_required
def eventplanning( request ):
"""
View for a specific user, to edit his events
"""
@ -70,14 +80,14 @@ def eventplanning_view( request ):
musician = get_object_or_404( Musician, user=request.user )
for e in all_future_events:
e.participation = EventParticipation.objects.get( event = e, musician = musician )
e.participation = EventParticipation.get_or_create( event = e, musician = musician )
context = { 'events' : all_future_events }
return render ( request, 'eventplanner/eventplanning_view.html', context )
@login_required
def events_grid( request ):
musicians = Musician.objects.all()
@ -87,17 +97,7 @@ def events_grid( request ):
all_future_events = list ( Event.objects.filter( date__gte = datetime.date.today() ) )
for e in all_future_events:
e.participation = [ EventParticipation.objects.get( event = e, musician = m ) for m in musicians ]
# Sort the participations in a dict
#for p in participationQs:
# username = p.musician.user.username
# eventId = p.event.pk
# if not eventId in grid:
# grid[eventId] = {}
#
# grid[eventId][username] = p
e.participation = [ EventParticipation.get_or_create( event = e, musician = m ) for m in musicians ]
context = { 'events': all_future_events,
'musicianNames' : musicianNames }
@ -107,6 +107,9 @@ def events_grid( request ):
# ------------------------------------ Detail Views ----------------------------------------------------
from django.views.generic.edit import UpdateView
@ -121,8 +124,27 @@ class EventUpdate( UpdateView ):
template_name_suffix = "_update_form"
success_url = '/events/grid'
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(EventUpdate, self).dispatch(request, *args, **kwargs)
# ------------------------------------ URLS ----------------------------------------------------
urls = patterns('',
url(r'^$', main_view ),
url(r'^grid$', events_grid ),
url(r'^planning$', eventplanning ),
url(r'^(?P<pk>\d+)$', EventUpdate.as_view() ),
url(r'^api/', event_api, name="event_api" ),
url(r'^api/(\w+)/$', event_api, name="event_api_per_user" ),
url(r'^api/(\w+)/(\d+)$', event_api, name="event_api_per_user_event"),
)

View File

@ -25,11 +25,19 @@ class Musician( models.Model ):
image = models.ImageField( upload_to = musicianPictureName, verbose_name=_("image") )
instrument = models.CharField( max_length=4, choices=INSTRUMENTS, blank=True, verbose_name=_("instrument") )
birthday = models.DateField( null=True, verbose_name=_("birthday") )
street = models.CharField( max_length=80, blank=True, verbose_name=_("street") )
city = models.CharField( max_length=40, blank=True, verbose_name=_("city") )
zip_code = models.IntegerField( null=True, verbose_name=_("zip_code") )
phone_home = models.CharField( max_length=18, blank=True, verbose_name=_("phone_home") )
phone_mobile = models.CharField( max_length=18, blank=True, verbose_name=_("phone_mobile") )
phone_work = models.CharField( max_length=18, blank=True, verbose_name=_("phone_work") )
public_description = models.TextField( blank=True, verbose_name=_("public_description") )
def __unicode__(self):

40
planning.txt Normal file
View File

@ -0,0 +1,40 @@
------------------ Outline ------------------------------------------
Internal / Normal User
- / MainPage - Either login view, or Main
- feature slider ( Terminplanung - Nachrichten )
- next event countdown
- /events Normale Planung
/events/api/... API
/events/<pk> ReadOnly view of event
/musicians/profile Own profile
/musicians All Contact information
Internal / Managing User
/ MainPage ( or login )
- feature slider ( new event, event grid )
- event grid
/events Displays Event Grid
/musicians
/musicians/create
( /musicians/<username> edit profile information )
------------------ TODO ------------------------------------------
- URL Conf into modules
- Access Rights
- new user type
- bootstrap customize: everything red