# -*- coding: utf-8 -*-
"""
The Views module provide view functions, which were called by the
`url dispatcher <https://docs.djangoproject.com/en/1.4/topics/http/urls/>`_,
and aggregate some data for use in templates.
"""
from datetime import datetime
from itertools import chain
from django.contrib.auth.models import User
from django.core import signing
from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail
from django.core.signing import Signer
from django.utils import simplejson
from django.utils.safestring import mark_safe
from django.views.decorators.http import require_POST
from django.contrib import messages
from utils import login_required
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.core.urlresolvers import reverse
from django.shortcuts import render, get_object_or_404
from django.utils.decorators import method_decorator
from django.views.generic.edit import CreateView
from filters import QuestFilter
from herobase.forms import QuestCreateForm, UserProfileEdit, UserProfilePrivacyEdit
from herobase.models import Quest, Adventure, CLASS_CHOICES, UserProfile
import logging
from django.db.models import Count, Sum
logger = logging.getLogger('youarehero.herobase')
@login_required
def quest_list_view(request, template='herobase/quest/list.html'):
"""Basic quest list, with django-filter app"""
f = QuestFilter(request.GET, queryset=Quest.objects.order_by('-created'))
return render(request, template, {
'filter': f,
'quests': f.qs,
})
[docs]class QuestCreateView(CreateView):
"""Basic Quest create view. This generic view-class should be refactored to a normal view function"""
context_object_name = "quest"
form_class = QuestCreateForm
template_name = "herobase/quest/create.html"
success_url = '../%(id)s/'
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(QuestCreateView, self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(QuestCreateView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url())
[docs]def quest_detail_view(request, quest_id, template="herobase/quest/detail.html"):
"""Render detail template for quest, and adventure if it exists."""
quest = get_object_or_404(Quest, pk=quest_id)
if not request.user.is_anonymous():
try:
adventure = quest.adventure_set.get(user=request.user)
except Adventure.DoesNotExist:
adventure = None
else:
adventure = None
context = {
'quest': quest,
'adventure': adventure,
}
return render(request, template, context)
[docs]def home_view(request):
"""Proxy view for switching between the hero and the public home view"""
if request.user.is_authenticated():
return hero_home_view(request)
return render(request, "herobase/public_home.html", {
'open_quests': Quest.objects.filter(state=Quest.STATE_OPEN)})
[docs]def m_home_view(request):
"""Proxy view for switching between the hero and the public home view"""
return hero_home_view(request, template='herobase/m/home.html')
# return render(request, "herobase/m/public_home.html", {'open_quests':
# Quest.objects.filter(state=Quest.STATE_OPEN)})
[docs]def abstract(request):
"""static you are hero abstract view."""
return render(request, "herobase/abstract.html")
[docs]def hero_classes(request):
"""static view for explaining hero classes."""
return render(request, "herobase/hero_classes.html")
@login_required
def hero_home_view(request, template='herobase/hero_home.html'):
"""the hero home is only visible for authenticated heros."""
user = request.user
return render(request, template,
{
#'profile': user.get_profile(),
'quests_active': user.created_quests.active().order_by('-created'),
'quests_old': user.created_quests.inactive().order_by('-created'),
'quests_joined': Quest.objects.active().filter(adventure__user=user).exclude(adventure__user=user, adventure__state=Adventure.STATE_HERO_CANCELED)
})
@login_required
def quest_my(request, template='herobase/quest/my.html'):
"""Views the quests the hero is envolved with."""
user = request.user
return render(request, template,
{
#'profile': user.get_profile(),
'quests_active': user.created_quests.active().order_by('-created'),
'quests_old': user.created_quests.inactive().order_by('-created'),
'quests_joined': Quest.objects.active().filter(adventure__user=user).exclude(adventure__user=user, adventure__state=Adventure.STATE_HERO_CANCELED)
})
@require_POST
@login_required
def quest_update(request, quest_id):
"""Handle POST data for quest-actions and redirect to quest-detail-view."""
quest = get_object_or_404(Quest, pk=quest_id)
if 'action' in request.POST:
action = request.POST['action']
try:
quest.process_action(request, action)
except ValueError, e:
messages.error(request, e.message)
except PermissionDenied, e:
messages.error(request, e.message)
else:
messages.error(request, 'No action submitted.')
return HttpResponseRedirect(reverse('quest-detail', args=(quest.pk, )))
@require_POST
@login_required
def adventure_update(request, quest_id):
"""Handle POST data for adventure-actions and redirect to quest-detail-view."""
quest = get_object_or_404(Quest, pk=quest_id)
adventure_id = request.POST.get('adventure_id')
adventure = get_object_or_404(quest.adventure_set, pk=adventure_id)
if 'action' in request.POST:
action = request.POST['action']
try:
adventure.process_action(request, action)
except ValueError, e:
messages.error(request, e.message)
except PermissionDenied, e:
messages.error(request, e.message)
else:
messages.error(request, 'No action submitted.')
return HttpResponseRedirect(reverse('quest-detail', args=(quest.pk, )))
@login_required
def userprofile(request, username=None, template='herobase/userprofile/detail.html'):
"""Render Userprofile with some stats."""
if username:
user = get_object_or_404(User, username=username)
else:
user = request.user
rank = UserProfile.objects.filter(experience__gt=user.get_profile().experience).count() + 1
hero_completed_quests = []
class_choices = dict(CLASS_CHOICES)
color_dict = { 5: '#e8e8e8',
1: '#ccffa7',
2: '#fff9b4',
3: '#ffa19b',
4: '#bdcaff'}
colors = []
for choice, count in user.adventures\
.filter(state=Adventure.STATE_OWNER_DONE)\
.values_list('quest__hero_class')\
.annotate(Count('quest__hero_class')):
colors.append(color_dict[choice])
hero_completed_quests.append((class_choices[choice], count))
return render(request, template, {
'user': user,
'rank': rank,
'colors': mark_safe(simplejson.dumps(colors)),
'completed_quest_count': user.adventures.filter(state=Adventure.STATE_OWNER_DONE).count(),
'hero_completed_quests': mark_safe(simplejson.dumps(hero_completed_quests)),
})
@login_required
def userprofile_edit(request):
"""Render the userprofile form and handle possible changes."""
user = request.user
form = UserProfileEdit(request.POST or None, instance=user.get_profile())
if form.is_valid():
form.save()
messages.success(request, 'Profile successfully changed')
return render(request, 'herobase/userprofile/edit.html', {
'form': form
})
@login_required
def userprofile_privacy_settings(request):
"""Render another userprofile form for privacy settings saved on the userprofile."""
user = request.user
form = UserProfilePrivacyEdit(request.POST or None, instance=user.get_profile())
if form.is_valid():
form.save()
messages.success(request, 'Privacy settings successfully changed')
return HttpResponseRedirect('.')
return render(request, 'herobase/userprofile/privacy_settings.html', {
'form': form
})
@login_required
def leader_board(request):
"""Render a view of the top heroes by rank."""
top_creators = User.objects.filter(created_quests__state=Quest.STATE_OWNER_DONE).annotate(quest_count=Count('created_quests')).filter(quest_count__gt=0).order_by('-quest_count')
local = None
if request.user.is_authenticated():
total, local = request.user.get_profile().get_related_leaderboard()
else:
total = User.objects.select_related().filter(userprofile__experience__gt=0).order_by('-userprofile__experience')
return render(request, "herobase/leader_board.html", {'total': total,
'local': local,
'top_creators': top_creators,
'myname': request.user.username
})
@login_required
def random_stats(request):
"""Some general stats"""
user = request.user
class_choices = dict(CLASS_CHOICES)
color_dict = { None: "#ff0000",
5: '#e8e8e8',
1: '#ccffa7',
2: '#fff9b4',
3: '#ffa19b',
4: '#bdcaff'}
hero_completed_quests = []
for choice, count in user.adventures\
.filter(quest__state=Quest.STATE_OWNER_DONE)\
.filter(state=Adventure.STATE_OWNER_DONE)\
.values_list('quest__hero_class')\
.annotate(Count('quest__hero_class')):
hero_completed_quests.append((class_choices.get(choice), count))
colors0 = []
open_quest_types = []
for choice, count in Quest.objects.filter(state=Quest.STATE_OPEN).values_list('hero_class').annotate(Count('hero_class')):
open_quest_types.append((class_choices.get(choice), count))
colors0.append(color_dict[choice])
colors1 = []
completed_quest_types = []
for choice, count in Quest.objects.filter(state=Quest.STATE_OWNER_DONE).values_list('hero_class').annotate(Count('hero_class')):
completed_quest_types.append((class_choices.get(choice) , count))
colors1.append(color_dict[choice])
context = {
'hero_completed_quests': hero_completed_quests,
'open_quest_types': open_quest_types,
'completed_quest_types': completed_quest_types,
'colors0': colors0,
'colors1': colors1,
}
for key in context:
context[key] = mark_safe(simplejson.dumps(list(context[key])))
context.update({
'quests_completed': user.adventures.filter(quest__state=Quest.STATE_OWNER_DONE).count()
})
return render(request, 'herobase/stats.html', context)
[docs]def signups(request):
"""Special view for nosy developers."""
if request.user.is_authenticated() and request.user.is_staff:
logged_in = '\n'.join('%s: %s' % (u.last_login, u.username) for u in User.objects.order_by('-last_login')[:20])
signed_up = '\n'.join('%s: %s' % (u.date_joined, u.username) for u in User.objects.order_by('-date_joined')[:10])
return HttpResponse('Logged in \n%s\nJoined\n%s' % (logged_in, signed_up), mimetype='text/plain')
else:
raise Http404()
def send_keep_account_mails():
return # TODO : test this, correct text, send mails
users = User.objects.filter(username='raphael')
mail_template = """\
Liebe Heldinnen, liebe Helden
Erstmal vielen Dank für das Erstellen eures Accounts und die Teilnahme
an dem Playtest bei der GPN.
Wir haben sehr viel Feedback bekommen und viele gute Anregungen für
die Weiterentwicklung der Plattform.
Wenn ihr weiter über YAH auf dem Laufenden gehalten werden wollt,
dann klickt bitte auf den Bestätigungslink.
{url}
Alle Accounts die nicht innerhalb der nächsten Woche bestätigen
werden wir wie angekündigt löschen.
VIELEN DANK FÜRS MITMACHEN und viel Spass bei all euren zukünftigen
Heldentaten - ob nun mit Plattform oder ohne :)
YAH!!!
"""
signer = Signer(salt='keep-email')
for user in users:
email = signer.sign(user.email)
relative_url = reverse('keep-email', args=(email,))
url = 'https://youarehero.net%s' % relative_url
mail_text = mail_template.format(url=url)
send_mail("You Are Hero", mail_text, from_email='noreply@youarehero.net', recipient_list=[user.email])
def confirm_keep_email(request, action):
signer = Signer(salt='keep-email')
try:
email = signer.unsign(action)
except signing.BadSignature:
return HttpResponse("I'm afraid i can't do that dave.", mimetype='text/plain')
user = User.objects.get(email=email)
profile = user.get_profile()
if profile.keep_email_after_gpn:
return HttpResponse("Already keeping email for %s" % email, mimetype='text/plain')
else:
profile.keep_email_after_gpn = datetime.now()
profile.save()
return HttpResponse("Keeping email for %s" % email, mimetype='text/plain')