Get rid of wrapt hack. Django 2.2 introduced setup() method on views. Yay!

This commit is contained in:
Víðir Valberg Guðmundsson 2019-04-02 12:32:12 +02:00
parent 63890131c8
commit 75c8db4577
4 changed files with 316 additions and 257 deletions

View file

@ -1,208 +1,176 @@
import os
import wrapt
import django.views
from .environment_settings import * from .environment_settings import *
def local_dir(entry): def local_dir(entry):
return os.path.join( return os.path.join(os.path.dirname(os.path.dirname(__file__)), entry)
os.path.dirname(os.path.dirname(__file__)),
entry
)
# We do this hacky monkeypatching to enable us to define a setup method
# on class based views for setting up variables without touching the dispatch
# method.
@wrapt.patch_function_wrapper(django.views.View, 'dispatch')
def monkey_patched_dispatch(wrapped, instance, args, kwargs):
if hasattr(instance, 'setup'):
instance.setup(*args, **kwargs)
return wrapped(*args, **kwargs)
DJANGO_BASE_PATH = os.path.dirname(os.path.dirname(__file__)) DJANGO_BASE_PATH = os.path.dirname(os.path.dirname(__file__))
WSGI_APPLICATION = 'bornhack.wsgi.application' WSGI_APPLICATION = "bornhack.wsgi.application"
ASGI_APPLICATION = 'bornhack.routing.application' ASGI_APPLICATION = "bornhack.routing.application"
ROOT_URLCONF = 'bornhack.urls' ROOT_URLCONF = "bornhack.urls"
ACCOUNT_ADAPTER = 'allauth_2fa.adapter.OTPAdapter' ACCOUNT_ADAPTER = "allauth_2fa.adapter.OTPAdapter"
SITE_ID = 1 SITE_ID = 1
ADMINS = ( ADMINS = (("bornhack sysadm", "sysadm@bornhack.org"),)
('bornhack sysadm', 'sysadm@bornhack.org'),
)
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', "django.contrib.admin",
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.staticfiles', "django.contrib.staticfiles",
'django.contrib.sites', "django.contrib.sites",
"graphene_django",
'graphene_django', "channels",
'channels', "corsheaders",
'corsheaders', "profiles",
"camps",
'profiles', "shop",
'camps', "news",
'shop', "utils",
'news', "villages",
'utils', "program",
'villages', "info",
'program', "sponsors",
'info', "ircbot",
'sponsors', "teams",
'ircbot', "people",
'teams', "tickets",
'people', "bar",
'tickets', "backoffice",
'bar', "events",
'backoffice', "rideshare",
'events', "tokens",
'rideshare', "feedback",
'tokens', "economy",
'feedback', "allauth",
'economy', "allauth.account",
"allauth_2fa",
'allauth', "django_otp",
'allauth.account', "django_otp.plugins.otp_totp",
'allauth_2fa', "django_otp.plugins.otp_static",
'django_otp', "bootstrap3",
'django_otp.plugins.otp_totp', "django_extensions",
'django_otp.plugins.otp_static', "reversion",
'bootstrap3', "betterforms",
'django_extensions',
'reversion',
'betterforms',
] ]
#MEDIA_URL = '/media/' # MEDIA_URL = '/media/'
STATIC_URL = '/static/' STATIC_URL = "/static/"
STATIC_ROOT = local_dir('static') STATIC_ROOT = local_dir("static")
STATICFILES_DIRS = [local_dir('static_src')] STATICFILES_DIRS = [local_dir("static_src")]
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = "en-us"
#USE_I18N = True # USE_I18N = True
#USE_L10N = True # USE_L10N = True
USE_TZ = True USE_TZ = True
SHORT_DATE_FORMAT = 'd/m-Y' SHORT_DATE_FORMAT = "d/m-Y"
DATE_FORMAT = 'd/m-Y' DATE_FORMAT = "d/m-Y"
DATETIME_FORMAT = 'd/m-Y H:i' DATETIME_FORMAT = "d/m-Y H:i"
TIME_FORMAT = 'H:i' TIME_FORMAT = "H:i"
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [local_dir('templates')], "DIRS": [local_dir("templates")],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.template.context_processors.media', "django.template.context_processors.media",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
'shop.context_processors.current_order', "shop.context_processors.current_order",
'camps.context_processors.camp', "camps.context_processors.camp",
], ]
}, },
}, }
] ]
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # Handles login to admin with username "django.contrib.auth.backends.ModelBackend", # Handles login to admin with username
'allauth.account.auth_backends.AuthenticationBackend', # Handles regular logins "allauth.account.auth_backends.AuthenticationBackend", # Handles regular logins
) )
ACCOUNT_AUTHENTICATION_METHOD = 'email' ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = True ACCOUNT_EMAIL_VERIFICATION = True
ACCOUNT_EMAIL_SUBJECT_PREFIX = '[bornhack] ' ACCOUNT_EMAIL_SUBJECT_PREFIX = "[bornhack] "
ACCOUNT_USERNAME_REQUIRED = False ACCOUNT_USERNAME_REQUIRED = False
LOGIN_REDIRECT_URL='/' LOGIN_REDIRECT_URL = "/"
LOGIN_URL = '/login/' LOGIN_URL = "/login/"
ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'https' ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
BOOTSTRAP3 = { BOOTSTRAP3 = {
'jquery_url': '/static/js/jquery.min.js', "jquery_url": "/static/js/jquery.min.js",
'javascript_url': '/static/js/bootstrap.min.js' "javascript_url": "/static/js/bootstrap.min.js",
} }
MIDDLEWARE = [ MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', "corsheaders.middleware.CorsMiddleware",
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django_otp.middleware.OTPMiddleware', "django_otp.middleware.OTPMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_ALLOW_ALL = True
CORS_URLS_REGEX = r'^/api/*$' CORS_URLS_REGEX = r"^/api/*$"
if DEBUG: if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
INSTALLED_APPS += [ INSTALLED_APPS += ["debug_toolbar"]
'debug_toolbar', MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
]
MIDDLEWARE = ['debug_toolbar.middleware.DebugToolbarMiddleware'] + MIDDLEWARE
INTERNAL_IPS = "127.0.0.1" INTERNAL_IPS = "127.0.0.1"
DEBUG_TOOLBAR_PANELS = [ DEBUG_TOOLBAR_PANELS = [
'debug_toolbar.panels.versions.VersionsPanel', "debug_toolbar.panels.versions.VersionsPanel",
'debug_toolbar.panels.timer.TimerPanel', "debug_toolbar.panels.timer.TimerPanel",
'debug_toolbar.panels.settings.SettingsPanel', "debug_toolbar.panels.settings.SettingsPanel",
'debug_toolbar.panels.headers.HeadersPanel', "debug_toolbar.panels.headers.HeadersPanel",
'debug_toolbar.panels.request.RequestPanel', "debug_toolbar.panels.request.RequestPanel",
'debug_toolbar.panels.sql.SQLPanel', "debug_toolbar.panels.sql.SQLPanel",
'debug_toolbar.panels.staticfiles.StaticFilesPanel', "debug_toolbar.panels.staticfiles.StaticFilesPanel",
'debug_toolbar.panels.templates.TemplatesPanel', "debug_toolbar.panels.templates.TemplatesPanel",
'debug_toolbar.panels.cache.CachePanel', "debug_toolbar.panels.cache.CachePanel",
'debug_toolbar.panels.signals.SignalsPanel', "debug_toolbar.panels.signals.SignalsPanel",
'debug_toolbar.panels.logging.LoggingPanel', "debug_toolbar.panels.logging.LoggingPanel",
'debug_toolbar.panels.redirects.RedirectsPanel', "debug_toolbar.panels.redirects.RedirectsPanel",
] ]
else: else:
SESSION_COOKIE_SECURE=True SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE=True CSRF_COOKIE_SECURE = True
LOGGING = { LOGGING = {
'version': 1, "version": 1,
'disable_existing_loggers': False, "disable_existing_loggers": False,
'formatters': { "formatters": {
'syslog': { "syslog": {"format": "%(levelname)s %(name)s.%(funcName)s(): %(message)s"},
'format': '%(levelname)s %(name)s.%(funcName)s(): %(message)s' "console": {
}, "format": "[%(asctime)s] %(name)s.%(funcName)s() %(levelname)s %(message)s",
'console': { "datefmt": "%d/%b/%Y %H:%M:%S",
'format': '[%(asctime)s] %(name)s.%(funcName)s() %(levelname)s %(message)s',
'datefmt': '%d/%b/%Y %H:%M:%S'
}, },
}, },
'handlers': { "handlers": {
'console': { "console": {
'level': 'DEBUG', "level": "DEBUG",
'class': 'logging.StreamHandler', "class": "logging.StreamHandler",
'formatter': 'console' "formatter": "console",
}, }
}, },
'loggers': { "loggers": {
# send the bornhack logger to console at DEBUG level, # send the bornhack logger to console at DEBUG level,
# do not propagate bornhack.* messages up to the root logger # do not propagate bornhack.* messages up to the root logger
'bornhack': { "bornhack": {"handlers": ["console"], "level": "DEBUG", "propagate": False}
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False,
},
}, },
} }
GRAPHENE = { GRAPHENE = {"SCHEMA": "bornhack.schema.schema"}
'SCHEMA': 'bornhack.schema.schema'
}

View file

@ -8,25 +8,21 @@ class ChainViewMixin(object):
""" """
The ChainViewMixin sets self.chain based on chain_slug from the URL The ChainViewMixin sets self.chain based on chain_slug from the URL
""" """
def setup(self, *args, **kwargs): def setup(self, *args, **kwargs):
if hasattr(super(), 'setup'): super().setup(*args, **kwargs)
super().setup(*args, **kwargs) self.chain = get_object_or_404(Chain, slug=self.kwargs["chain_slug"])
self.chain = get_object_or_404(
Chain,
slug=self.kwargs["chain_slug"],
)
class CredebtorViewMixin(object): class CredebtorViewMixin(object):
""" """
The CredebtorViewMixin sets self.credebtor based on credebtor_slug from the URL The CredebtorViewMixin sets self.credebtor based on credebtor_slug from the URL
""" """
def setup(self, *args, **kwargs): def setup(self, *args, **kwargs):
if hasattr(super(), 'setup'): super().setup(*args, **kwargs)
super().setup(*args, **kwargs)
self.credebtor = get_object_or_404( self.credebtor = get_object_or_404(
Credebtor, Credebtor, slug=self.kwargs["credebtor_slug"]
slug=self.kwargs["credebtor_slug"],
) )
@ -34,9 +30,12 @@ class ExpensePermissionMixin(object):
""" """
This mixin checks if request.user submitted the Expense, or if request.user has camps.economyteam_permission This mixin checks if request.user submitted the Expense, or if request.user has camps.economyteam_permission
""" """
def get_object(self, queryset=None): def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset) obj = super().get_object(queryset=queryset)
if obj.user == self.request.user or self.request.user.has_perm('camps.economyteam_permission'): if obj.user == self.request.user or self.request.user.has_perm(
"camps.economyteam_permission"
):
return obj return obj
else: else:
# the current user is different from the user who submitted the expense, and current user is not in the economy team; fuckery is afoot, no thanks # the current user is different from the user who submitted the expense, and current user is not in the economy team; fuckery is afoot, no thanks
@ -47,9 +46,12 @@ class RevenuePermissionMixin(object):
""" """
This mixin checks if request.user submitted the Revenue, or if request.user has camps.economyteam_permission This mixin checks if request.user submitted the Revenue, or if request.user has camps.economyteam_permission
""" """
def get_object(self, queryset=None): def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset) obj = super().get_object(queryset=queryset)
if obj.user == self.request.user or self.request.user.has_perm('camps.economyteam_permission'): if obj.user == self.request.user or self.request.user.has_perm(
"camps.economyteam_permission"
):
return obj return obj
else: else:
# the current user is different from the user who submitted the revenue, and current user is not in the economy team; fuckery is afoot, no thanks # the current user is different from the user who submitted the revenue, and current user is not in the economy team; fuckery is afoot, no thanks
@ -60,11 +62,13 @@ class ReimbursementPermissionMixin(object):
""" """
This mixin checks if request.user owns the Reimbursement, or if request.user has camps.economyteam_permission This mixin checks if request.user owns the Reimbursement, or if request.user has camps.economyteam_permission
""" """
def get_object(self, queryset=None): def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset) obj = super().get_object(queryset=queryset)
if obj.reimbursement_user == self.request.user or self.request.user.has_perm('camps.economyteam_permission'): if obj.reimbursement_user == self.request.user or self.request.user.has_perm(
"camps.economyteam_permission"
):
return obj return obj
else: else:
# the current user is different from the user who "owns" the reimbursement, and current user is not in the economy team; fuckery is afoot, no thanks # the current user is different from the user who "owns" the reimbursement, and current user is not in the economy team; fuckery is afoot, no thanks
raise Http404() raise Http404()

View file

@ -6,7 +6,14 @@ from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.urls import reverse from django.urls import reverse
from django.views.generic import CreateView, ListView, DetailView, TemplateView, UpdateView, DeleteView from django.views.generic import (
CreateView,
ListView,
DetailView,
TemplateView,
UpdateView,
DeleteView,
)
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Sum from django.db.models import Sum
@ -20,7 +27,7 @@ from .forms import *
class EconomyDashboardView(LoginRequiredMixin, CampViewMixin, TemplateView): class EconomyDashboardView(LoginRequiredMixin, CampViewMixin, TemplateView):
template_name = 'dashboard.html' template_name = "dashboard.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
""" """
@ -29,27 +36,55 @@ class EconomyDashboardView(LoginRequiredMixin, CampViewMixin, TemplateView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
# get reimbursement stats # get reimbursement stats
context['reimbursement_count'] = Reimbursement.objects.filter(reimbursement_user=self.request.user, camp=self.camp).count() context["reimbursement_count"] = Reimbursement.objects.filter(
context['unpaid_reimbursement_count'] = Reimbursement.objects.filter(reimbursement_user=self.request.user, paid=False, camp=self.camp).count() reimbursement_user=self.request.user, camp=self.camp
context['paid_reimbursement_count'] = Reimbursement.objects.filter(reimbursement_user=self.request.user, paid=True, camp=self.camp).count() ).count()
context["unpaid_reimbursement_count"] = Reimbursement.objects.filter(
reimbursement_user=self.request.user, paid=False, camp=self.camp
).count()
context["paid_reimbursement_count"] = Reimbursement.objects.filter(
reimbursement_user=self.request.user, paid=True, camp=self.camp
).count()
reimbursement_total = 0 reimbursement_total = 0
for reimbursement in Reimbursement.objects.filter(reimbursement_user=self.request.user, camp=self.camp): for reimbursement in Reimbursement.objects.filter(
reimbursement_user=self.request.user, camp=self.camp
):
reimbursement_total += reimbursement.amount reimbursement_total += reimbursement.amount
context['reimbursement_total'] = reimbursement_total context["reimbursement_total"] = reimbursement_total
# get expense stats # get expense stats
context['expense_count'] = Expense.objects.filter(user=self.request.user, camp=self.camp).count() context["expense_count"] = Expense.objects.filter(
context['unapproved_expense_count'] = Expense.objects.filter(user=self.request.user, approved__isnull=True, camp=self.camp).count() user=self.request.user, camp=self.camp
context['approved_expense_count'] = Expense.objects.filter(user=self.request.user, approved=True, camp=self.camp).count() ).count()
context['rejected_expense_count'] = Expense.objects.filter(user=self.request.user, approved=False, camp=self.camp).count() context["unapproved_expense_count"] = Expense.objects.filter(
context['expense_total'] = Expense.objects.filter(user=self.request.user, camp=self.camp).aggregate(Sum('amount'))['amount__sum'] user=self.request.user, approved__isnull=True, camp=self.camp
).count()
context["approved_expense_count"] = Expense.objects.filter(
user=self.request.user, approved=True, camp=self.camp
).count()
context["rejected_expense_count"] = Expense.objects.filter(
user=self.request.user, approved=False, camp=self.camp
).count()
context["expense_total"] = Expense.objects.filter(
user=self.request.user, camp=self.camp
).aggregate(Sum("amount"))["amount__sum"]
# get revenue stats # get revenue stats
context['revenue_count'] = Revenue.objects.filter(user=self.request.user, camp=self.camp).count() context["revenue_count"] = Revenue.objects.filter(
context['unapproved_revenue_count'] = Revenue.objects.filter(user=self.request.user, approved__isnull=True, camp=self.camp).count() user=self.request.user, camp=self.camp
context['approved_revenue_count'] = Revenue.objects.filter(user=self.request.user, approved=True, camp=self.camp).count() ).count()
context['rejected_revenue_count'] = Revenue.objects.filter(user=self.request.user, approved=False, camp=self.camp).count() context["unapproved_revenue_count"] = Revenue.objects.filter(
context['revenue_total'] = Revenue.objects.filter(user=self.request.user, camp=self.camp).aggregate(Sum('amount'))['amount__sum'] user=self.request.user, approved__isnull=True, camp=self.camp
).count()
context["approved_revenue_count"] = Revenue.objects.filter(
user=self.request.user, approved=True, camp=self.camp
).count()
context["rejected_revenue_count"] = Revenue.objects.filter(
user=self.request.user, approved=False, camp=self.camp
).count()
context["revenue_total"] = Revenue.objects.filter(
user=self.request.user, camp=self.camp
).aggregate(Sum("amount"))["amount__sum"]
return context return context
@ -59,9 +94,9 @@ class EconomyDashboardView(LoginRequiredMixin, CampViewMixin, TemplateView):
class ChainCreateView(CampViewMixin, RaisePermissionRequiredMixin, CreateView): class ChainCreateView(CampViewMixin, RaisePermissionRequiredMixin, CreateView):
model = Chain model = Chain
template_name = 'chain_create.html' template_name = "chain_create.html"
permission_required = ("camps.expense_create_permission") permission_required = "camps.expense_create_permission"
fields = ['name', 'notes'] fields = ["name", "notes"]
def form_valid(self, form): def form_valid(self, form):
chain = form.save() chain = form.save()
@ -69,33 +104,38 @@ class ChainCreateView(CampViewMixin, RaisePermissionRequiredMixin, CreateView):
# a message for the user # a message for the user
messages.success( messages.success(
self.request, self.request,
"The new Chain %s has been saved. You can now add Creditor(s)/Debtor(s) for it." % chain.name, "The new Chain %s has been saved. You can now add Creditor(s)/Debtor(s) for it."
% chain.name,
) )
return HttpResponseRedirect(reverse('economy:credebtor_create', kwargs={ return HttpResponseRedirect(
'camp_slug': self.camp.slug, reverse(
'chain_slug': chain.slug, "economy:credebtor_create",
})) kwargs={"camp_slug": self.camp.slug, "chain_slug": chain.slug},
)
)
class ChainListView(CampViewMixin, RaisePermissionRequiredMixin, ListView): class ChainListView(CampViewMixin, RaisePermissionRequiredMixin, ListView):
model = Chain model = Chain
template_name = 'chain_list.html' template_name = "chain_list.html"
permission_required = ("camps.expense_create_permission") permission_required = "camps.expense_create_permission"
class CredebtorCreateView(CampViewMixin, ChainViewMixin, RaisePermissionRequiredMixin, CreateView): class CredebtorCreateView(
CampViewMixin, ChainViewMixin, RaisePermissionRequiredMixin, CreateView
):
model = Credebtor model = Credebtor
template_name = 'credebtor_create.html' template_name = "credebtor_create.html"
permission_required = ("camps.expense_create_permission") permission_required = "camps.expense_create_permission"
fields = ['name', 'address', 'notes'] fields = ["name", "address", "notes"]
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
""" """
Add chain to context Add chain to context
""" """
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['chain'] = self.chain context["chain"] = self.chain
return context return context
def form_valid(self, form): def form_valid(self, form):
@ -106,26 +146,31 @@ class CredebtorCreateView(CampViewMixin, ChainViewMixin, RaisePermissionRequired
# a message for the user # a message for the user
messages.success( messages.success(
self.request, self.request,
"The Creditor/Debtor %s has been saved. You can now add Expenses/Revenues for it." % credebtor.name, "The Creditor/Debtor %s has been saved. You can now add Expenses/Revenues for it."
% credebtor.name,
) )
return HttpResponseRedirect(reverse('economy:credebtor_list', kwargs={ return HttpResponseRedirect(
'camp_slug': self.camp.slug, reverse(
'chain_slug': self.chain.slug, "economy:credebtor_list",
})) kwargs={"camp_slug": self.camp.slug, "chain_slug": self.chain.slug},
)
)
class CredebtorListView(CampViewMixin, ChainViewMixin, RaisePermissionRequiredMixin, ListView): class CredebtorListView(
CampViewMixin, ChainViewMixin, RaisePermissionRequiredMixin, ListView
):
model = Credebtor model = Credebtor
template_name = 'credebtor_list.html' template_name = "credebtor_list.html"
permission_required = ("camps.expense_create_permission") permission_required = "camps.expense_create_permission"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
""" """
Add chain to context Add chain to context
""" """
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['chain'] = self.chain context["chain"] = self.chain
return context return context
@ -134,7 +179,7 @@ class CredebtorListView(CampViewMixin, ChainViewMixin, RaisePermissionRequiredMi
class ExpenseListView(LoginRequiredMixin, CampViewMixin, ListView): class ExpenseListView(LoginRequiredMixin, CampViewMixin, ListView):
model = Expense model = Expense
template_name = 'expense_list.html' template_name = "expense_list.html"
def get_queryset(self): def get_queryset(self):
# only return Expenses belonging to the current user # only return Expenses belonging to the current user
@ -143,13 +188,15 @@ class ExpenseListView(LoginRequiredMixin, CampViewMixin, ListView):
class ExpenseDetailView(CampViewMixin, ExpensePermissionMixin, DetailView): class ExpenseDetailView(CampViewMixin, ExpensePermissionMixin, DetailView):
model = Expense model = Expense
template_name = 'expense_detail.html' template_name = "expense_detail.html"
class ExpenseCreateView(CampViewMixin, CredebtorViewMixin, RaisePermissionRequiredMixin, CreateView): class ExpenseCreateView(
CampViewMixin, CredebtorViewMixin, RaisePermissionRequiredMixin, CreateView
):
model = Expense model = Expense
template_name = 'expense_form.html' template_name = "expense_form.html"
permission_required = ("camps.expense_create_permission") permission_required = "camps.expense_create_permission"
form_class = ExpenseCreateForm form_class = ExpenseCreateForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -157,8 +204,10 @@ class ExpenseCreateView(CampViewMixin, CredebtorViewMixin, RaisePermissionRequir
Do not show teams that are not part of the current camp in the dropdown Do not show teams that are not part of the current camp in the dropdown
""" """
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['form'].fields['responsible_team'].queryset = Team.objects.filter(camp=self.camp) context["form"].fields["responsible_team"].queryset = Team.objects.filter(
context['creditor'] = self.credebtor camp=self.camp
)
context["creditor"] = self.credebtor
return context return context
def form_valid(self, form): def form_valid(self, form):
@ -178,24 +227,32 @@ class ExpenseCreateView(CampViewMixin, CredebtorViewMixin, RaisePermissionRequir
add_outgoing_email( add_outgoing_email(
"emails/expense_awaiting_approval_email.txt", "emails/expense_awaiting_approval_email.txt",
formatdict=dict(expense=expense), formatdict=dict(expense=expense),
subject="New %s expense for %s Team is awaiting approval" % (expense.camp.title, expense.responsible_team.name), subject="New %s expense for %s Team is awaiting approval"
% (expense.camp.title, expense.responsible_team.name),
to_recipients=[settings.ECONOMYTEAM_EMAIL], to_recipients=[settings.ECONOMYTEAM_EMAIL],
) )
# return to the expense list page # return to the expense list page
return HttpResponseRedirect(reverse('economy:expense_list', kwargs={'camp_slug': self.camp.slug})) return HttpResponseRedirect(
reverse("economy:expense_list", kwargs={"camp_slug": self.camp.slug})
)
class ExpenseUpdateView(CampViewMixin, ExpensePermissionMixin, UpdateView): class ExpenseUpdateView(CampViewMixin, ExpensePermissionMixin, UpdateView):
model = Expense model = Expense
template_name = 'expense_form.html' template_name = "expense_form.html"
form_class = ExpenseUpdateForm form_class = ExpenseUpdateForm
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs) response = super().dispatch(request, *args, **kwargs)
if self.get_object().approved: if self.get_object().approved:
messages.error(self.request, "This expense has already been approved, it cannot be updated") messages.error(
return redirect(reverse('economy:expense_list', kwargs={'camp_slug': self.camp.slug})) self.request,
"This expense has already been approved, it cannot be updated",
)
return redirect(
reverse("economy:expense_list", kwargs={"camp_slug": self.camp.slug})
)
return response return response
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -203,24 +260,31 @@ class ExpenseUpdateView(CampViewMixin, ExpensePermissionMixin, UpdateView):
Do not show teams that are not part of the current camp in the dropdown Do not show teams that are not part of the current camp in the dropdown
""" """
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['form'].fields['responsible_team'].queryset = Team.objects.filter(camp=self.camp) context["form"].fields["responsible_team"].queryset = Team.objects.filter(
context['creditor'] = self.get_object().creditor camp=self.camp
)
context["creditor"] = self.get_object().creditor
return context return context
def get_success_url(self): def get_success_url(self):
messages.success(self.request, "Expense %s has been updated" % self.get_object().pk) messages.success(
return(reverse('economy:expense_list', kwargs={'camp_slug': self.camp.slug})) self.request, "Expense %s has been updated" % self.get_object().pk
)
return reverse("economy:expense_list", kwargs={"camp_slug": self.camp.slug})
class ExpenseDeleteView(CampViewMixin, ExpensePermissionMixin, UpdateView): class ExpenseDeleteView(CampViewMixin, ExpensePermissionMixin, UpdateView):
model = Expense model = Expense
template_name = 'expense_delete.html' template_name = "expense_delete.html"
fields = [] fields = []
def form_valid(self, form): def form_valid(self, form):
expense = self.get_object() expense = self.get_object()
if expense.approved: if expense.approved:
messages.error(self.request, "This expense has already been approved, it cannot be deleted") messages.error(
self.request,
"This expense has already been approved, it cannot be deleted",
)
else: else:
message = "Expense %s has been deleted" % expense.pk message = "Expense %s has been deleted" % expense.pk
expense.delete() expense.delete()
@ -228,7 +292,7 @@ class ExpenseDeleteView(CampViewMixin, ExpensePermissionMixin, UpdateView):
return redirect(self.get_success_url()) return redirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):
return(reverse('economy:expense_list', kwargs={'camp_slug': self.camp.slug})) return reverse("economy:expense_list", kwargs={"camp_slug": self.camp.slug})
class ExpenseInvoiceView(CampViewMixin, ExpensePermissionMixin, DetailView): class ExpenseInvoiceView(CampViewMixin, ExpensePermissionMixin, DetailView):
@ -236,6 +300,7 @@ class ExpenseInvoiceView(CampViewMixin, ExpensePermissionMixin, DetailView):
This view returns the invoice for an Expense with the proper mimetype This view returns the invoice for an Expense with the proper mimetype
Uses ExpensePermissionMixin to make sure the user is allowed to see the image Uses ExpensePermissionMixin to make sure the user is allowed to see the image
""" """
model = Expense model = Expense
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -246,9 +311,11 @@ class ExpenseInvoiceView(CampViewMixin, ExpensePermissionMixin, DetailView):
# find mimetype # find mimetype
mimetype = magic.from_buffer(invoicedata, mime=True) mimetype = magic.from_buffer(invoicedata, mime=True)
# check if we have a PDF, no preview if so, load a pdf icon instead # check if we have a PDF, no preview if so, load a pdf icon instead
if mimetype=="application/pdf" and 'preview' in request.GET: if mimetype == "application/pdf" and "preview" in request.GET:
invoicedata = open(os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/pdf.png"), "rb").read() invoicedata = open(
mimetype = magic.from_buffer(invoicedata, mime=True) os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/pdf.png"), "rb"
).read()
mimetype = magic.from_buffer(invoicedata, mime=True)
# put the response together and return it # put the response together and return it
response = HttpResponse(content_type=mimetype) response = HttpResponse(content_type=mimetype)
response.write(invoicedata) response.write(invoicedata)
@ -260,7 +327,7 @@ class ExpenseInvoiceView(CampViewMixin, ExpensePermissionMixin, DetailView):
class ReimbursementListView(LoginRequiredMixin, CampViewMixin, ListView): class ReimbursementListView(LoginRequiredMixin, CampViewMixin, ListView):
model = Reimbursement model = Reimbursement
template_name = 'reimbursement_list.html' template_name = "reimbursement_list.html"
def get_queryset(self): def get_queryset(self):
# only return Expenses belonging to the current user # only return Expenses belonging to the current user
@ -269,7 +336,7 @@ class ReimbursementListView(LoginRequiredMixin, CampViewMixin, ListView):
class ReimbursementDetailView(CampViewMixin, ReimbursementPermissionMixin, DetailView): class ReimbursementDetailView(CampViewMixin, ReimbursementPermissionMixin, DetailView):
model = Reimbursement model = Reimbursement
template_name = 'reimbursement_detail.html' template_name = "reimbursement_detail.html"
########### Revenue related views ############### ########### Revenue related views ###############
@ -277,7 +344,7 @@ class ReimbursementDetailView(CampViewMixin, ReimbursementPermissionMixin, Detai
class RevenueListView(LoginRequiredMixin, CampViewMixin, ListView): class RevenueListView(LoginRequiredMixin, CampViewMixin, ListView):
model = Revenue model = Revenue
template_name = 'revenue_list.html' template_name = "revenue_list.html"
def get_queryset(self): def get_queryset(self):
# only return Revenues belonging to the current user # only return Revenues belonging to the current user
@ -286,13 +353,15 @@ class RevenueListView(LoginRequiredMixin, CampViewMixin, ListView):
class RevenueDetailView(CampViewMixin, RevenuePermissionMixin, DetailView): class RevenueDetailView(CampViewMixin, RevenuePermissionMixin, DetailView):
model = Revenue model = Revenue
template_name = 'revenue_detail.html' template_name = "revenue_detail.html"
class RevenueCreateView(CampViewMixin, CredebtorViewMixin, RaisePermissionRequiredMixin, CreateView): class RevenueCreateView(
CampViewMixin, CredebtorViewMixin, RaisePermissionRequiredMixin, CreateView
):
model = Revenue model = Revenue
template_name = 'revenue_form.html' template_name = "revenue_form.html"
permission_required = ("camps.revenue_create_permission") permission_required = "camps.revenue_create_permission"
form_class = RevenueCreateForm form_class = RevenueCreateForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -300,8 +369,10 @@ class RevenueCreateView(CampViewMixin, CredebtorViewMixin, RaisePermissionRequir
Do not show teams that are not part of the current camp in the dropdown Do not show teams that are not part of the current camp in the dropdown
""" """
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['form'].fields['responsible_team'].queryset = Team.objects.filter(camp=self.camp) context["form"].fields["responsible_team"].queryset = Team.objects.filter(
context['debtor'] = self.credebtor camp=self.camp
)
context["debtor"] = self.credebtor
return context return context
def form_valid(self, form): def form_valid(self, form):
@ -321,24 +392,32 @@ class RevenueCreateView(CampViewMixin, CredebtorViewMixin, RaisePermissionRequir
add_outgoing_email( add_outgoing_email(
"emails/revenue_awaiting_approval_email.txt", "emails/revenue_awaiting_approval_email.txt",
formatdict=dict(revenue=revenue), formatdict=dict(revenue=revenue),
subject="New %s revenue for %s Team is awaiting approval" % (revenue.camp.title, revenue.responsible_team.name), subject="New %s revenue for %s Team is awaiting approval"
% (revenue.camp.title, revenue.responsible_team.name),
to_recipients=[settings.ECONOMYTEAM_EMAIL], to_recipients=[settings.ECONOMYTEAM_EMAIL],
) )
# return to the revenue list page # return to the revenue list page
return HttpResponseRedirect(reverse('economy:revenue_list', kwargs={'camp_slug': self.camp.slug})) return HttpResponseRedirect(
reverse("economy:revenue_list", kwargs={"camp_slug": self.camp.slug})
)
class RevenueUpdateView(CampViewMixin, RevenuePermissionMixin, UpdateView): class RevenueUpdateView(CampViewMixin, RevenuePermissionMixin, UpdateView):
model = Revenue model = Revenue
template_name = 'revenue_form.html' template_name = "revenue_form.html"
form_class = RevenueUpdateForm form_class = RevenueUpdateForm
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs) response = super().dispatch(request, *args, **kwargs)
if self.get_object().approved: if self.get_object().approved:
messages.error(self.request, "This revenue has already been approved, it cannot be updated") messages.error(
return redirect(reverse('economy:revenue_list', kwargs={'camp_slug': self.camp.slug})) self.request,
"This revenue has already been approved, it cannot be updated",
)
return redirect(
reverse("economy:revenue_list", kwargs={"camp_slug": self.camp.slug})
)
return response return response
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -346,23 +425,30 @@ class RevenueUpdateView(CampViewMixin, RevenuePermissionMixin, UpdateView):
Do not show teams that are not part of the current camp in the dropdown Do not show teams that are not part of the current camp in the dropdown
""" """
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['form'].fields['responsible_team'].queryset = Team.objects.filter(camp=self.camp) context["form"].fields["responsible_team"].queryset = Team.objects.filter(
camp=self.camp
)
return context return context
def get_success_url(self): def get_success_url(self):
messages.success(self.request, "Revenue %s has been updated" % self.get_object().pk) messages.success(
return(reverse('economy:revenue_list', kwargs={'camp_slug': self.camp.slug})) self.request, "Revenue %s has been updated" % self.get_object().pk
)
return reverse("economy:revenue_list", kwargs={"camp_slug": self.camp.slug})
class RevenueDeleteView(CampViewMixin, RevenuePermissionMixin, UpdateView): class RevenueDeleteView(CampViewMixin, RevenuePermissionMixin, UpdateView):
model = Revenue model = Revenue
template_name = 'revenue_delete.html' template_name = "revenue_delete.html"
fields = [] fields = []
def form_valid(self, form): def form_valid(self, form):
revenue = self.get_object() revenue = self.get_object()
if revenue.approved: if revenue.approved:
messages.error(self.request, "This revenue has already been approved, it cannot be deleted") messages.error(
self.request,
"This revenue has already been approved, it cannot be deleted",
)
else: else:
message = "Revenue %s has been deleted" % revenue.pk message = "Revenue %s has been deleted" % revenue.pk
revenue.delete() revenue.delete()
@ -370,7 +456,7 @@ class RevenueDeleteView(CampViewMixin, RevenuePermissionMixin, UpdateView):
return redirect(self.get_success_url()) return redirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):
return(reverse('economy:revenue_list', kwargs={'camp_slug': self.camp.slug})) return reverse("economy:revenue_list", kwargs={"camp_slug": self.camp.slug})
class RevenueInvoiceView(CampViewMixin, RevenuePermissionMixin, DetailView): class RevenueInvoiceView(CampViewMixin, RevenuePermissionMixin, DetailView):
@ -378,6 +464,7 @@ class RevenueInvoiceView(CampViewMixin, RevenuePermissionMixin, DetailView):
This view returns a http response with the invoice for a Revenue object, with the proper mimetype This view returns a http response with the invoice for a Revenue object, with the proper mimetype
Uses RevenuePermissionMixin to make sure the user is allowed to see the file Uses RevenuePermissionMixin to make sure the user is allowed to see the file
""" """
model = Revenue model = Revenue
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -388,11 +475,12 @@ class RevenueInvoiceView(CampViewMixin, RevenuePermissionMixin, DetailView):
# find mimetype # find mimetype
mimetype = magic.from_buffer(invoicedata, mime=True) mimetype = magic.from_buffer(invoicedata, mime=True)
# check if we have a PDF, no preview if so, load a pdf icon instead # check if we have a PDF, no preview if so, load a pdf icon instead
if mimetype=="application/pdf" and 'preview' in request.GET: if mimetype == "application/pdf" and "preview" in request.GET:
invoicedata = open(os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/pdf.png"), "rb").read() invoicedata = open(
mimetype = magic.from_buffer(invoicedata, mime=True) os.path.join(settings.DJANGO_BASE_PATH, "static_src/img/pdf.png"), "rb"
).read()
mimetype = magic.from_buffer(invoicedata, mime=True)
# put the response together and return it # put the response together and return it
response = HttpResponse(content_type=mimetype) response = HttpResponse(content_type=mimetype)
response.write(invoicedata) response.write(invoicedata)
return response return response

View file

@ -38,5 +38,4 @@ six==1.12.0
sqlparse==0.3.0 sqlparse==0.3.0
venusian==1.2.0 venusian==1.2.0
webencodings==0.5.1 webencodings==0.5.1
wrapt==1.11.1
graphene-django==2.2.0 graphene-django==2.2.0