diff --git a/src/events/managers.py b/src/events/managers.py index d845727..ceeda5a 100644 --- a/src/events/managers.py +++ b/src/events/managers.py @@ -31,6 +31,10 @@ class EventManager(models.Manager): """Returns all past events.""" return self.filter(start__lt=now()) + def latest(self, limit=None): + result = self.filter(start__lt=now()).order_by('-start', '-end') + return result[0:limit] if limit else result + def upcoming(self, limit=None): """Returns the next 'limit' upcoming events. diff --git a/src/events/mixins.py b/src/events/mixins.py index cb8f372..9b9f915 100644 --- a/src/events/mixins.py +++ b/src/events/mixins.py @@ -1,4 +1,6 @@ """Mixins for Events.""" +from django.http import Http404 + from . import models @@ -9,7 +11,6 @@ class EventArchiveMixin(object): date_field = 'start' make_object_list = True model = models.Event - ordering = ('start', 'end') paginate_by = 15 template_name = 'events/event_archive.html' @@ -40,3 +41,16 @@ class EventDetailMixin(object): elif hasattr(self, 'object') and hasattr(self.object, 'event'): context['event'] = self.object.event return context + + def get_queryset(self): + """set event attribute from the URL kwarg event and + load all related objects from the set model. + + :return: a django QuerySets + """ + try: + self.event = models.Event.objects.get(pk=self.kwargs['event']) + queryset = self.model.objects.filter(event=self.event) + except models.Event.DoesNotExist: + raise Http404(_('Event does not exist')) + return queryset.prefetch_related() diff --git a/src/events/views.py b/src/events/views.py index 1dfa8a0..e92bd3a 100644 --- a/src/events/views.py +++ b/src/events/views.py @@ -30,7 +30,6 @@ class DeleteEventPhoto(PermissionRequiredMixin, mixins.EventDetailMixin, class EventArchiveIndex(mixins.EventArchiveMixin, generic.ArchiveIndexView): """Index of the event archive, displays the upcoming events first.""" allow_empty = True - ordering = ('-start', '-end') class EventArchiveMonth(mixins.EventArchiveMixin, generic.MonthArchiveView): @@ -73,7 +72,7 @@ class EventForm(PermissionRequiredMixin, mixins.EventDetailMixin, if self.kwargs.get('pk') else models.Event() -class EventGallery(mixins.EventDetailMixin, generic.ListView): +class EventGallery(generic.ListView): """Display a overview of all event photo albums.""" template_name = 'events/photo_gallery.html' queryset = models.Event.objects.filter( diff --git a/src/mahjong_ranking/managers.py b/src/mahjong_ranking/managers.py index 1ed70f8..08560b4 100644 --- a/src/mahjong_ranking/managers.py +++ b/src/mahjong_ranking/managers.py @@ -13,7 +13,7 @@ class HanchanManager(models.Manager): """ use_for_related_fields = True - def confirmed_hanchans(self, user=None, since=None, until=None, **filter_args): + def confirmed(self, user=None, since=None, until=None, **filter_args): """ Return all valid and confirmed Hanchans. :param user: Only return Hanchans where this user participated. @@ -85,7 +85,7 @@ class HanchanManager(models.Manager): season = season or until.year except AttributeError: season = date.today().year - return self.confirmed_hanchans(user=user, season=season, until=until) + return self.confirmed(user=user, season=season, until=until) def user_hanchans(self, user, since=None, until=None, **filter_args): """Return all Hanchans where a specific user has participated. @@ -108,7 +108,7 @@ class HanchanManager(models.Manager): [hanchan.get_playerdata(user) for hanchan in queryset] return queryset - def unconfirmed_hanchans(self, user=None, **filter_args): + def unconfirmed(self, user=None, **filter_args): """ Return all Hanchans that have been set to unconfirmed. :param user: Only return Hanchans where this user participated. diff --git a/src/mahjong_ranking/mixins.py b/src/mahjong_ranking/mixins.py new file mode 100644 index 0000000..f621d8a --- /dev/null +++ b/src/mahjong_ranking/mixins.py @@ -0,0 +1,18 @@ +from datetime import date +from .models import Hanchan, SeasonRanking +from events.models import Event + + +class MahjongMixin(object): + def get_context_data(self, **kwargs): + context = super(MahjongMixin, self).get_context_data(**kwargs) + try: + context['season'] = self.season + context['season_start'] = date(year=self.season, month=1, day=1) + context['season_end'] = date(year=self.season, month=12, day=31) + context['season_list'] = SeasonRanking.objects.season_list + except AttributeError: + pass + context['latest_hanchan_list'] = Hanchan.objects.confirmed()[:3] + context['latest_event_list'] = Event.objects.latest(limit=3) + return context diff --git a/src/mahjong_ranking/models.py b/src/mahjong_ranking/models.py index 2335ca8..e31bff7 100644 --- a/src/mahjong_ranking/models.py +++ b/src/mahjong_ranking/models.py @@ -56,7 +56,7 @@ class EventRanking(models.Model): ) sum_placement = 0.0 sum_score = 0.0 - event_hanchans = Hanchan.objects.confirmed_hanchans( + event_hanchans = Hanchan.objects.confirmed( user=self.user_id, event=self.event_id ) @@ -441,7 +441,7 @@ class KyuDanRanking(models.Model): Fetches all valid Hanchans from this Player and recalculates his Kyu/Dan Ranking. """ - valid_hanchans = Hanchan.objects.confirmed_hanchans(user=self.user) + valid_hanchans = Hanchan.objects.confirmed(user=self.user) valid_hanchans = valid_hanchans.order_by('start') if since and self.last_hanchan_date and since < self.last_hanchan_date: force_recalc = True diff --git a/src/mahjong_ranking/tests.py b/src/mahjong_ranking/tests.py index 22c2d56..ca85a6c 100644 --- a/src/mahjong_ranking/tests.py +++ b/src/mahjong_ranking/tests.py @@ -54,7 +54,7 @@ class KyuDanTest(TestCase): for ranking in KyuDanRanking.objects.all(): original = {a: getattr(ranking, a) for a in self.equal_attrs} - confirmed_hanchans = Hanchan.objects.confirmed_hanchans( + confirmed_hanchans = Hanchan.objects.confirmed( user=ranking.user, since=ranking.legacy_date ) @@ -86,7 +86,7 @@ class KyuDanTest(TestCase): 'dan_points': ranking.legacy_dan_points or 0, 'kyu_points': ranking.legacy_kyu_points or 0 } - confirmed_hanchans = Hanchan.objects.confirmed_hanchans( + confirmed_hanchans = Hanchan.objects.confirmed( user=ranking.user, since=ranking.legacy_date ) diff --git a/src/mahjong_ranking/views.py b/src/mahjong_ranking/views.py index 2c121cc..5394070 100644 --- a/src/mahjong_ranking/views.py +++ b/src/mahjong_ranking/views.py @@ -11,11 +11,11 @@ from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ from django.views import generic -from events.models import Event from events.mixins import EventDetailMixin from . import forms, models +from .mixins import MahjongMixin -kyu_dan_order = { +KYU_DAN_ORDER = { # map sort URL args to Django ORM order_by args '+full_name': ('user__last_name', 'user__first_name'), '-full_name': ('-user__last_name', '-user__first_name'), '+hanchan_count': ('hanchan_count',), @@ -31,16 +31,17 @@ kyu_dan_order = { class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, generic.DeleteView): - """ - Fragt zuerst nach, ob die Hanchan wirklich gelöscht werden soll. - Wir die Frage mit "Ja" beantwortet, wird die die Hanchan gelöscht. - """ + """Deletes a Hanchan if confimration has been answerd with 'yes'.""" form_class = forms.HanchanForm model = models.Hanchan permission_required = 'mahjong_ranking.delete_hanchan' pk_url_kwarg = 'hanchan' def get_success_url(self): + """ + Return to the HachanList of the event form the deleted hanchan. + :return: URL of the EventHanchanList for the event + """ return reverse('event-hanchan-list', kwargs={'event': self.object.event.pk}) @@ -48,32 +49,30 @@ class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin, generic.UpdateView): """ - Ein Formular um neue Hanchans anzulegen, bzw. eine bestehende zu - bearbeitsen + A Form to add a new or edit an existing Hanchan. """ form_class = forms.HanchanForm model = models.Hanchan permission_required = 'mahjong_ranking.add_hanchan' - def get_context_data(self, **kwargs): - context = generic.UpdateView.get_context_data(self, **kwargs) - context['event'] = self.event - return context - def get_form_class(self): """ - Users with edit Persmission will see the AdminForm to confirm - unconfirmed Hanchans. + Users with hanchan edit persmission can also un-/confirm hanchans. + :return: forms.HanchanForm, or forms.HanchanAdminForm """ - if self.request.user.has_perm('mahjong_ranking.change_hanchan'): - return forms.HanchanAdminForm - else: - return forms.HanchanForm + return forms.HanchanAdminForm if self.request.user.has_perm( + 'mahjong_ranking.change_hanchan') else forms.HanchanForm def get_object(self, queryset=None): + """ + load the hanchan form the db, or create a new one with the event set. + Also sets the event attribute. + :param queryset: + :return: models.Hanchan object + """ if self.kwargs.get('hanchan') and self.request.user.has_perm( 'mahjong_ranking.change_hanchan'): - hanchan = models.Hanchan.objects.get(id=self.kwargs['hanchan']) + hanchan = self.model.objects.get(id=self.kwargs['hanchan']) self.event = hanchan.event elif self.kwargs.get('event'): self.event = models.Event.objects.get(id=self.kwargs['event']) @@ -94,6 +93,11 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin, return reverse('add-hanchan-form', kwargs={'event': self.event.pk}) def get_success_message(self, cleaned_data): + """ + Get the right sucsess message for the django notification subsystem. + :param cleaned_data: + :return: Sucsess message + """ if self.kwargs.get('hanchan'): return _('%s has been updated successfully.') % self.object else: @@ -103,72 +107,36 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin, class EventHanchanList(EventDetailMixin, generic.ListView): - """ - Auflistung aller Hanchan die während der Veranstaltung gespielt wurden. - """ + "List all hanchans played on a given event." model = models.Hanchan template_name = 'mahjong_ranking/eventhanchan_list.html' - def get_queryset(self): - try: - self.event = models.Event.objects.get(pk=self.kwargs['event']) - queryset = models.Hanchan.objects.filter(event=self.event) - queryset = queryset.order_by('start') - return queryset - except models.Event.DoesNotExist: - raise django.http.Http404(_('Event does not exist')) - class EventRankingList(EventDetailMixin, generic.ListView): - """ - Anzeige des Eventrankings, daß erstellt wurde falls der Termin als internes - Turnier markiert wurde. - """ + """Display the event ranking for the given event.""" model = models.EventRanking - def get_queryset(self): - try: - self.event = models.Event.objects.get(pk=self.kwargs['event']) - queryset = models.EventRanking.objects.filter(event=self.event) - return queryset.prefetch_related() - except models.Event.DoesNotExist: - raise django.http.Http404(_('Event does not exist')) -class MahjongMixin(object): - def get_context_data(self, **kwargs): - context = super(MahjongMixin, self).get_context_data(**kwargs) - try: - context['season'] = self.season - context['season_start'] = date(year=self.season, month=1, day=1) - context['season_end'] = date(year=self.season, month=12, day=31) - context['season_list'] = models.SeasonRanking.objects.season_list - except AttributeError: - pass - context[ - 'latest_hanchan_list'] = \ - models.Hanchan.objects.confirmed_hanchans()[ - :3] - context['latest_event_list'] = Event.objects.upcoming(limit=3) - return context class KyuDanRankingList(MahjongMixin, generic.ListView): - """ - Anzeige aller Spiele mit ihrem Kyu bzw Dan Grad. - """ + """List all Players with an Kyu or Dan score. """ default_order = '-score' order_by = '' paginate_by = 25 def dispatch(self, request, *args, **kwargs): - self.order_by = kyu_dan_order[ + """Set the order_by settings, revert to default_order if necessary.""" + self.order_by = KYU_DAN_ORDER[ kwargs.get('order_by', self.default_order) ] - return generic.ListView.dispatch(self, request, *args, **kwargs) + return super(KyuDanRankingList, self).dispatch(request, *args, **kwargs) + def get_queryset(self): - queryset = models.KyuDanRanking.objects.all().order_by(*self.order_by) + queryset = models.KyuDanRanking.objects.filter( + hanchan_count__gt=0).order_by(*self.order_by) return queryset.select_related() @@ -224,14 +192,15 @@ class PlayerDanScore(PlayerScore): def get_queryset(self): kyu_dan_ranking = models.KyuDanRanking.objects.get(user=self.user) - return models.Hanchan.objects.dan_hanchans(user=self.user, since=kyu_dan_ranking.legacy_date) + return models.Hanchan.objects.dan_hanchans(user=self.user, + since=kyu_dan_ranking.legacy_date) class PlayerInvalidScore(PlayerScore): template_name = 'mahjong_ranking/player_invalid_score.html' def get_queryset(self): - return models.Hanchan.objects.unconfirmed_hanchans(user=self.user) + return models.Hanchan.objects.unconfirmed(user=self.user) class PlayerKyuScore(PlayerScore):