run pre-commit --all-files, it's been a while since last time
This commit is contained in:
parent
a21bc1097c
commit
01687ea11a
|
@ -1,4 +1,5 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from program.models import Event, Speaker
|
from program.models import Event, Speaker
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ class AutoScheduleApplyForm(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class EventScheduleForm(forms.Form):
|
class EventScheduleForm(forms.Form):
|
||||||
""" The EventSlots are added in the view and help_text is not visible, just define the field """
|
"""The EventSlots are added in the view and help_text is not visible, just define the field"""
|
||||||
|
|
||||||
slot = forms.ChoiceField()
|
slot = forms.ChoiceField()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from economy.models import Pos
|
from economy.models import Pos
|
||||||
from utils.mixins import RaisePermissionRequiredMixin
|
from utils.mixins import RaisePermissionRequiredMixin
|
||||||
|
|
||||||
|
|
|
@ -395,7 +395,9 @@ urlpatterns = [
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path(
|
path(
|
||||||
"", SpeakerDetailView.as_view(), name="speaker_detail",
|
"",
|
||||||
|
SpeakerDetailView.as_view(),
|
||||||
|
name="speaker_detail",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"update/",
|
"update/",
|
||||||
|
@ -473,7 +475,11 @@ urlpatterns = [
|
||||||
"<slug:slug>/",
|
"<slug:slug>/",
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path("", EventDetailView.as_view(), name="event_detail",),
|
path(
|
||||||
|
"",
|
||||||
|
EventDetailView.as_view(),
|
||||||
|
name="event_detail",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"update/",
|
"update/",
|
||||||
EventUpdateView.as_view(),
|
EventUpdateView.as_view(),
|
||||||
|
@ -500,7 +506,11 @@ urlpatterns = [
|
||||||
"autoscheduler/",
|
"autoscheduler/",
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path("", AutoScheduleManageView.as_view(), name="autoschedule_manage",),
|
path(
|
||||||
|
"",
|
||||||
|
AutoScheduleManageView.as_view(),
|
||||||
|
name="autoschedule_manage",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"crashcourse/",
|
"crashcourse/",
|
||||||
AutoScheduleCrashCourseView.as_view(),
|
AutoScheduleCrashCourseView.as_view(),
|
||||||
|
@ -512,7 +522,9 @@ urlpatterns = [
|
||||||
name="autoschedule_validate",
|
name="autoschedule_validate",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"diff/", AutoScheduleDiffView.as_view(), name="autoschedule_diff",
|
"diff/",
|
||||||
|
AutoScheduleDiffView.as_view(),
|
||||||
|
name="autoschedule_diff",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"apply/",
|
"apply/",
|
||||||
|
@ -539,7 +551,11 @@ urlpatterns = [
|
||||||
name="approve_event_feedback",
|
name="approve_event_feedback",
|
||||||
),
|
),
|
||||||
# add recording url objects
|
# add recording url objects
|
||||||
path("add_recording", AddRecordingView.as_view(), name="add_eventrecording",),
|
path(
|
||||||
|
"add_recording",
|
||||||
|
AddRecordingView.as_view(),
|
||||||
|
name="add_eventrecording",
|
||||||
|
),
|
||||||
# economy
|
# economy
|
||||||
path(
|
path(
|
||||||
"economy/",
|
"economy/",
|
||||||
|
@ -658,18 +674,34 @@ urlpatterns = [
|
||||||
"pos/",
|
"pos/",
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path("", PosListView.as_view(), name="pos_list",),
|
path(
|
||||||
path("create/", PosCreateView.as_view(), name="pos_create",),
|
"",
|
||||||
|
PosListView.as_view(),
|
||||||
|
name="pos_list",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"create/",
|
||||||
|
PosCreateView.as_view(),
|
||||||
|
name="pos_create",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"<slug:pos_slug>/",
|
"<slug:pos_slug>/",
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path("", PosDetailView.as_view(), name="pos_detail",),
|
|
||||||
path(
|
path(
|
||||||
"update/", PosUpdateView.as_view(), name="pos_update",
|
"",
|
||||||
|
PosDetailView.as_view(),
|
||||||
|
name="pos_detail",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"delete/", PosDeleteView.as_view(), name="pos_delete",
|
"update/",
|
||||||
|
PosUpdateView.as_view(),
|
||||||
|
name="pos_update",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"delete/",
|
||||||
|
PosDeleteView.as_view(),
|
||||||
|
name="pos_delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"reports/",
|
"reports/",
|
||||||
|
@ -731,14 +763,30 @@ urlpatterns = [
|
||||||
"tokens/",
|
"tokens/",
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path("", TokenListView.as_view(), name="token_list",),
|
path(
|
||||||
path("create/", TokenCreateView.as_view(), name="token_create",),
|
"",
|
||||||
path("stats/", TokenStatsView.as_view(), name="token_stats",),
|
TokenListView.as_view(),
|
||||||
|
name="token_list",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"create/",
|
||||||
|
TokenCreateView.as_view(),
|
||||||
|
name="token_create",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"stats/",
|
||||||
|
TokenStatsView.as_view(),
|
||||||
|
name="token_stats",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"<int:pk>/",
|
"<int:pk>/",
|
||||||
include(
|
include(
|
||||||
[
|
[
|
||||||
path("", TokenDetailView.as_view(), name="token_detail",),
|
path(
|
||||||
|
"",
|
||||||
|
TokenDetailView.as_view(),
|
||||||
|
name="token_detail",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"update/",
|
"update/",
|
||||||
TokenUpdateView.as_view(),
|
TokenUpdateView.as_view(),
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
"""Backoffice views.py was split into multiple files in July 2021 /tyk."""
|
"""Backoffice views.py was split into multiple files in July 2021 /tyk."""
|
||||||
from .backoffice import * # noqa
|
from .backoffice import * # noqa
|
||||||
from .content import * # noqa
|
from .content import * # noqa
|
||||||
from .economy import * # noqa
|
from .economy import * # noqa
|
||||||
from .facilities import * # noqa
|
from .facilities import * # noqa
|
||||||
from .game import * # noqa
|
from .game import * # noqa
|
||||||
from .infodesk import * # noqa
|
from .infodesk import * # noqa
|
||||||
from .orga import * # noqa
|
from .orga import * # noqa
|
||||||
from .pos import * # noqa
|
from .pos import * # noqa
|
||||||
from .program import * # noqa
|
from .program import * # noqa
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from facilities.models import (
|
|
||||||
FacilityFeedback,
|
from camps.mixins import CampViewMixin
|
||||||
)
|
from facilities.models import FacilityFeedback
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
from utils.models import OutgoingEmail
|
from utils.models import OutgoingEmail
|
||||||
|
|
||||||
from ..mixins import (
|
from ..mixins import RaisePermissionRequiredMixin
|
||||||
RaisePermissionRequiredMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
class BackofficeIndexView(CampViewMixin, RaisePermissionRequiredMixin, TemplateView):
|
class BackofficeIndexView(CampViewMixin, RaisePermissionRequiredMixin, TemplateView):
|
||||||
"""
|
"""
|
||||||
The Backoffice index view only requires camps.backoffice_permission so we use RaisePermissionRequiredMixin directly
|
The Backoffice index view only requires camps.backoffice_permission so we use RaisePermissionRequiredMixin directly
|
||||||
|
@ -37,7 +35,14 @@ class BackofficeIndexView(CampViewMixin, RaisePermissionRequiredMixin, TemplateV
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
context["held_email_count"] = OutgoingEmail.objects.filter(hold=True, responsible_team__isnull=True).count() + OutgoingEmail.objects.filter(hold=True, responsible_team__camp=self.camp).count()
|
context["held_email_count"] = (
|
||||||
|
OutgoingEmail.objects.filter(
|
||||||
|
hold=True, responsible_team__isnull=True
|
||||||
|
).count()
|
||||||
|
+ OutgoingEmail.objects.filter(
|
||||||
|
hold=True, responsible_team__camp=self.camp
|
||||||
|
).count()
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,7 +56,7 @@ class BackofficeProxyView(CampViewMixin, RaisePermissionRequiredMixin, TemplateV
|
||||||
template_name = "backoffice_proxy.html"
|
template_name = "backoffice_proxy.html"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
""" Perform the request and return the response if we have a slug """
|
"""Perform the request and return the response if we have a slug"""
|
||||||
# list available stuff if we have no slug
|
# list available stuff if we have no slug
|
||||||
if "proxy_slug" not in kwargs:
|
if "proxy_slug" not in kwargs:
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
|
@ -1,24 +1,16 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.forms import modelformset_factory
|
from django.forms import modelformset_factory
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
from program.models import (
|
|
||||||
Event,
|
|
||||||
EventFeedback,
|
|
||||||
Url,
|
|
||||||
UrlType,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ..forms import (
|
from camps.mixins import CampViewMixin
|
||||||
AddRecordingForm,
|
from program.models import Event, EventFeedback, Url, UrlType
|
||||||
)
|
|
||||||
from ..mixins import (
|
from ..forms import AddRecordingForm
|
||||||
ContentTeamPermissionMixin,
|
from ..mixins import ContentTeamPermissionMixin
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
@ -84,9 +76,7 @@ class AddRecordingView(CampViewMixin, ContentTeamPermissionMixin, FormView):
|
||||||
super().setup(*args, **kwargs)
|
super().setup(*args, **kwargs)
|
||||||
self.queryset = Event.objects.filter(
|
self.queryset = Event.objects.filter(
|
||||||
track__camp=self.camp, video_recording=True
|
track__camp=self.camp, video_recording=True
|
||||||
).exclude(
|
).exclude(urls__url_type__name="Recording")
|
||||||
urls__url_type__name="Recording"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.form_class = modelformset_factory(
|
self.form_class = modelformset_factory(
|
||||||
Event,
|
Event,
|
||||||
|
@ -113,19 +103,17 @@ class AddRecordingView(CampViewMixin, ContentTeamPermissionMixin, FormView):
|
||||||
form.save()
|
form.save()
|
||||||
|
|
||||||
for event_data in form.cleaned_data:
|
for event_data in form.cleaned_data:
|
||||||
if event_data['recording_url']:
|
if event_data["recording_url"]:
|
||||||
url = event_data['recording_url']
|
url = event_data["recording_url"]
|
||||||
if not event_data['id'].urls.filter(url=url).exists():
|
if not event_data["id"].urls.filter(url=url).exists():
|
||||||
recording_url = Url()
|
recording_url = Url()
|
||||||
recording_url.event = event_data['id']
|
recording_url.event = event_data["id"]
|
||||||
recording_url.url = url
|
recording_url.url = url
|
||||||
recording_url.url_type = UrlType.objects.get(name="Recording")
|
recording_url.url_type = UrlType.objects.get(name="Recording")
|
||||||
recording_url.save()
|
recording_url.save()
|
||||||
|
|
||||||
if form.changed_objects:
|
if form.changed_objects:
|
||||||
messages.success(
|
messages.success(self.request, f"Updated {len(form.changed_objects)} Event")
|
||||||
self.request, f"Updated {len(form.changed_objects)} Event"
|
|
||||||
)
|
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
def get_success_url(self, *args, **kwargs):
|
def get_success_url(self, *args, **kwargs):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
@ -12,18 +11,12 @@ from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||||
from economy.models import (
|
|
||||||
Chain,
|
from camps.mixins import CampViewMixin
|
||||||
Credebtor,
|
from economy.models import Chain, Credebtor, Expense, Reimbursement, Revenue
|
||||||
Expense,
|
|
||||||
Reimbursement,
|
|
||||||
Revenue,
|
|
||||||
)
|
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
|
|
||||||
from ..mixins import (
|
from ..mixins import EconomyTeamPermissionMixin
|
||||||
EconomyTeamPermissionMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
@ -245,7 +238,7 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
|
||||||
fields = ["notes", "paid"]
|
fields = ["notes", "paid"]
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
""" Get the user from kwargs """
|
"""Get the user from kwargs"""
|
||||||
self.reimbursement_user = get_object_or_404(User, pk=kwargs["user_id"])
|
self.reimbursement_user = get_object_or_404(User, pk=kwargs["user_id"])
|
||||||
|
|
||||||
# get response now so we have self.camp available below
|
# get response now so we have self.camp available below
|
||||||
|
@ -448,5 +441,3 @@ class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
|
||||||
return redirect(
|
return redirect(
|
||||||
reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug})
|
reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
@ -9,19 +8,18 @@ from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||||
|
from leaflet.forms.widgets import LeafletWidget
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from facilities.models import (
|
from facilities.models import (
|
||||||
Facility,
|
Facility,
|
||||||
FacilityFeedback,
|
FacilityFeedback,
|
||||||
FacilityOpeningHours,
|
FacilityOpeningHours,
|
||||||
FacilityType,
|
FacilityType,
|
||||||
)
|
)
|
||||||
from leaflet.forms.widgets import LeafletWidget
|
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
|
|
||||||
from ..mixins import (
|
from ..mixins import OrgaTeamPermissionMixin, RaisePermissionRequiredMixin
|
||||||
OrgaTeamPermissionMixin,
|
|
||||||
RaisePermissionRequiredMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
@ -320,5 +318,3 @@ class FacilityOpeningHoursDeleteView(
|
||||||
"backoffice:facility_detail",
|
"backoffice:facility_detail",
|
||||||
kwargs={"camp_slug": self.camp.slug, "facility_uuid": self.facility.pk},
|
kwargs={"camp_slug": self.camp.slug, "facility_uuid": self.facility.pk},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
|
@ -8,11 +7,11 @@ from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from tokens.models import Token, TokenFind
|
from tokens.models import Token, TokenFind
|
||||||
|
|
||||||
from ..mixins import (
|
from ..mixins import RaisePermissionRequiredMixin
|
||||||
RaisePermissionRequiredMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import logging
|
import logging
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.views.generic import ListView, TemplateView
|
from django.views.generic import ListView, TemplateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from shop.models import Order, OrderProductRelation
|
from shop.models import Order, OrderProductRelation
|
||||||
from tickets.models import DiscountTicket, ShopTicket, SponsorTicket, TicketType
|
from tickets.models import DiscountTicket, ShopTicket, SponsorTicket, TicketType
|
||||||
|
|
||||||
from ..mixins import (
|
from ..mixins import InfoTeamPermissionMixin
|
||||||
InfoTeamPermissionMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
@ -140,7 +139,9 @@ class ScanTicketsView(
|
||||||
messages.success(request, "Order #{} has been marked as paid!".format(order.id))
|
messages.success(request, "Order #{} has been marked as paid!".format(order.id))
|
||||||
|
|
||||||
|
|
||||||
class ShopTicketOverview(LoginRequiredMixin, InfoTeamPermissionMixin, CampViewMixin, ListView):
|
class ShopTicketOverview(
|
||||||
|
LoginRequiredMixin, InfoTeamPermissionMixin, CampViewMixin, ListView
|
||||||
|
):
|
||||||
model = ShopTicket
|
model = ShopTicket
|
||||||
template_name = "shop_ticket_overview.html"
|
template_name = "shop_ticket_overview.html"
|
||||||
context_object_name = "shop_tickets"
|
context_object_name = "shop_tickets"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.forms import modelformset_factory
|
from django.forms import modelformset_factory
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
@ -8,13 +7,13 @@ from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic import ListView, TemplateView
|
from django.views.generic import ListView, TemplateView
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from profiles.models import Profile
|
from profiles.models import Profile
|
||||||
from shop.models import OrderProductRelation
|
from shop.models import OrderProductRelation
|
||||||
from utils.models import OutgoingEmail
|
from utils.models import OutgoingEmail
|
||||||
|
|
||||||
from ..mixins import (
|
from ..mixins import OrgaTeamPermissionMixin
|
||||||
OrgaTeamPermissionMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
@ -28,6 +27,7 @@ class ApproveNamesView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
|
||||||
public_credit_name=""
|
public_credit_name=""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# MERCHANDISE VIEWS
|
# MERCHANDISE VIEWS
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,17 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||||
from economy.models import (
|
|
||||||
Pos,
|
from camps.mixins import CampViewMixin
|
||||||
PosReport,
|
from economy.models import Pos, PosReport
|
||||||
)
|
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
|
|
||||||
from ..mixins import (
|
from ..mixins import OrgaTeamPermissionMixin, PosViewMixin, RaisePermissionRequiredMixin
|
||||||
OrgaTeamPermissionMixin,
|
|
||||||
PosViewMixin,
|
|
||||||
RaisePermissionRequiredMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
@ -10,6 +9,8 @@ from django.urls import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.views.generic import DetailView, ListView, TemplateView
|
from django.views.generic import DetailView, ListView, TemplateView
|
||||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from program.autoscheduler import AutoScheduler
|
from program.autoscheduler import AutoScheduler
|
||||||
from program.mixins import AvailabilityMatrixViewMixin
|
from program.mixins import AvailabilityMatrixViewMixin
|
||||||
from program.models import (
|
from program.models import (
|
||||||
|
@ -30,9 +31,7 @@ from ..forms import (
|
||||||
EventScheduleForm,
|
EventScheduleForm,
|
||||||
SpeakerForm,
|
SpeakerForm,
|
||||||
)
|
)
|
||||||
from ..mixins import (
|
from ..mixins import ContentTeamPermissionMixin
|
||||||
ContentTeamPermissionMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
@ -42,7 +41,7 @@ logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
class PendingProposalsView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class PendingProposalsView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This convenience view shows a list of pending proposals """
|
"""This convenience view shows a list of pending proposals"""
|
||||||
|
|
||||||
model = SpeakerProposal
|
model = SpeakerProposal
|
||||||
template_name = "pending_proposals.html"
|
template_name = "pending_proposals.html"
|
||||||
|
@ -88,7 +87,7 @@ class ProposalApproveBaseView(CampViewMixin, ContentTeamPermissionMixin, UpdateV
|
||||||
|
|
||||||
|
|
||||||
class SpeakerProposalListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class SpeakerProposalListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This view permits Content Team members to list SpeakerProposals """
|
"""This view permits Content Team members to list SpeakerProposals"""
|
||||||
|
|
||||||
model = SpeakerProposal
|
model = SpeakerProposal
|
||||||
template_name = "speaker_proposal_list.html"
|
template_name = "speaker_proposal_list.html"
|
||||||
|
@ -105,7 +104,7 @@ class SpeakerProposalDetailView(
|
||||||
ContentTeamPermissionMixin,
|
ContentTeamPermissionMixin,
|
||||||
DetailView,
|
DetailView,
|
||||||
):
|
):
|
||||||
""" This view permits Content Team members to see SpeakerProposal details """
|
"""This view permits Content Team members to see SpeakerProposal details"""
|
||||||
|
|
||||||
model = SpeakerProposal
|
model = SpeakerProposal
|
||||||
template_name = "speaker_proposal_detail_backoffice.html"
|
template_name = "speaker_proposal_detail_backoffice.html"
|
||||||
|
@ -118,7 +117,7 @@ class SpeakerProposalDetailView(
|
||||||
|
|
||||||
|
|
||||||
class SpeakerProposalApproveRejectView(ProposalApproveBaseView):
|
class SpeakerProposalApproveRejectView(ProposalApproveBaseView):
|
||||||
""" This view allows ContentTeam members to approve/reject SpeakerProposals """
|
"""This view allows ContentTeam members to approve/reject SpeakerProposals"""
|
||||||
|
|
||||||
model = SpeakerProposal
|
model = SpeakerProposal
|
||||||
template_name = "speaker_proposal_approve_reject.html"
|
template_name = "speaker_proposal_approve_reject.html"
|
||||||
|
@ -126,7 +125,7 @@ class SpeakerProposalApproveRejectView(ProposalApproveBaseView):
|
||||||
|
|
||||||
|
|
||||||
class EventProposalListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class EventProposalListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This view permits Content Team members to list EventProposals """
|
"""This view permits Content Team members to list EventProposals"""
|
||||||
|
|
||||||
model = EventProposal
|
model = EventProposal
|
||||||
template_name = "event_proposal_list.html"
|
template_name = "event_proposal_list.html"
|
||||||
|
@ -147,7 +146,7 @@ class EventProposalListView(CampViewMixin, ContentTeamPermissionMixin, ListView)
|
||||||
|
|
||||||
|
|
||||||
class EventProposalDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
class EventProposalDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
||||||
""" This view permits Content Team members to see EventProposal details """
|
"""This view permits Content Team members to see EventProposal details"""
|
||||||
|
|
||||||
model = EventProposal
|
model = EventProposal
|
||||||
template_name = "event_proposal_detail_backoffice.html"
|
template_name = "event_proposal_detail_backoffice.html"
|
||||||
|
@ -155,7 +154,7 @@ class EventProposalDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailV
|
||||||
|
|
||||||
|
|
||||||
class EventProposalApproveRejectView(ProposalApproveBaseView):
|
class EventProposalApproveRejectView(ProposalApproveBaseView):
|
||||||
""" This view allows ContentTeam members to approve/reject EventProposals """
|
"""This view allows ContentTeam members to approve/reject EventProposals"""
|
||||||
|
|
||||||
model = EventProposal
|
model = EventProposal
|
||||||
template_name = "event_proposal_approve_reject.html"
|
template_name = "event_proposal_approve_reject.html"
|
||||||
|
@ -167,7 +166,7 @@ class EventProposalApproveRejectView(ProposalApproveBaseView):
|
||||||
|
|
||||||
|
|
||||||
class SpeakerListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class SpeakerListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This view is used by the Content Team to see Speaker objects. """
|
"""This view is used by the Content Team to see Speaker objects."""
|
||||||
|
|
||||||
model = Speaker
|
model = Speaker
|
||||||
template_name = "speaker_list_backoffice.html"
|
template_name = "speaker_list_backoffice.html"
|
||||||
|
@ -186,7 +185,7 @@ class SpeakerListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
class SpeakerDetailView(
|
class SpeakerDetailView(
|
||||||
AvailabilityMatrixViewMixin, ContentTeamPermissionMixin, DetailView
|
AvailabilityMatrixViewMixin, ContentTeamPermissionMixin, DetailView
|
||||||
):
|
):
|
||||||
""" This view is used by the Content Team to see details for Speaker objects """
|
"""This view is used by the Content Team to see details for Speaker objects"""
|
||||||
|
|
||||||
model = Speaker
|
model = Speaker
|
||||||
template_name = "speaker_detail_backoffice.html"
|
template_name = "speaker_detail_backoffice.html"
|
||||||
|
@ -202,20 +201,20 @@ class SpeakerDetailView(
|
||||||
class SpeakerUpdateView(
|
class SpeakerUpdateView(
|
||||||
AvailabilityMatrixViewMixin, ContentTeamPermissionMixin, UpdateView
|
AvailabilityMatrixViewMixin, ContentTeamPermissionMixin, UpdateView
|
||||||
):
|
):
|
||||||
""" This view is used by the Content Team to update Speaker objects """
|
"""This view is used by the Content Team to update Speaker objects"""
|
||||||
|
|
||||||
model = Speaker
|
model = Speaker
|
||||||
template_name = "speaker_update.html"
|
template_name = "speaker_update.html"
|
||||||
form_class = SpeakerForm
|
form_class = SpeakerForm
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
""" Set camp for the form """
|
"""Set camp for the form"""
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs.update({"camp": self.camp})
|
kwargs.update({"camp": self.camp})
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
""" Save object and availability """
|
"""Save object and availability"""
|
||||||
speaker = form.save()
|
speaker = form.save()
|
||||||
save_speaker_availability(form, obj=speaker)
|
save_speaker_availability(form, obj=speaker)
|
||||||
messages.success(self.request, "Speaker has been updated")
|
messages.success(self.request, "Speaker has been updated")
|
||||||
|
@ -228,7 +227,7 @@ class SpeakerUpdateView(
|
||||||
|
|
||||||
|
|
||||||
class SpeakerDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
|
class SpeakerDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
|
||||||
""" This view is used by the Content Team to delete Speaker objects """
|
"""This view is used by the Content Team to delete Speaker objects"""
|
||||||
|
|
||||||
model = Speaker
|
model = Speaker
|
||||||
template_name = "speaker_delete.html"
|
template_name = "speaker_delete.html"
|
||||||
|
@ -252,7 +251,7 @@ class SpeakerDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
|
||||||
|
|
||||||
|
|
||||||
class EventTypeListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class EventTypeListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This view is used by the Content Team to list EventTypes """
|
"""This view is used by the Content Team to list EventTypes"""
|
||||||
|
|
||||||
model = EventType
|
model = EventType
|
||||||
template_name = "event_type_list.html"
|
template_name = "event_type_list.html"
|
||||||
|
@ -282,7 +281,7 @@ class EventTypeListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
|
|
||||||
|
|
||||||
class EventTypeDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
class EventTypeDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
||||||
""" This view is used by the Content Team to see details for EventTypes """
|
"""This view is used by the Content Team to see details for EventTypes"""
|
||||||
|
|
||||||
model = EventType
|
model = EventType
|
||||||
template_name = "event_type_detail.html"
|
template_name = "event_type_detail.html"
|
||||||
|
@ -306,7 +305,7 @@ class EventTypeDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView)
|
||||||
|
|
||||||
|
|
||||||
class EventLocationListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class EventLocationListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This view is used by the Content Team to list EventLocation objects. """
|
"""This view is used by the Content Team to list EventLocation objects."""
|
||||||
|
|
||||||
model = EventLocation
|
model = EventLocation
|
||||||
template_name = "event_location_list.html"
|
template_name = "event_location_list.html"
|
||||||
|
@ -319,7 +318,7 @@ class EventLocationListView(CampViewMixin, ContentTeamPermissionMixin, ListView)
|
||||||
|
|
||||||
|
|
||||||
class EventLocationDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
class EventLocationDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
||||||
""" This view is used by the Content Team to see details for EventLocation objects """
|
"""This view is used by the Content Team to see details for EventLocation objects"""
|
||||||
|
|
||||||
model = EventLocation
|
model = EventLocation
|
||||||
template_name = "event_location_detail.html"
|
template_name = "event_location_detail.html"
|
||||||
|
@ -334,7 +333,7 @@ class EventLocationDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailV
|
||||||
|
|
||||||
|
|
||||||
class EventLocationCreateView(CampViewMixin, ContentTeamPermissionMixin, CreateView):
|
class EventLocationCreateView(CampViewMixin, ContentTeamPermissionMixin, CreateView):
|
||||||
""" This view is used by the Content Team to create EventLocation objects """
|
"""This view is used by the Content Team to create EventLocation objects"""
|
||||||
|
|
||||||
model = EventLocation
|
model = EventLocation
|
||||||
fields = ["name", "icon", "capacity", "conflicts"]
|
fields = ["name", "icon", "capacity", "conflicts"]
|
||||||
|
@ -362,7 +361,7 @@ class EventLocationCreateView(CampViewMixin, ContentTeamPermissionMixin, CreateV
|
||||||
|
|
||||||
|
|
||||||
class EventLocationUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
class EventLocationUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
||||||
""" This view is used by the Content Team to update EventLocation objects """
|
"""This view is used by the Content Team to update EventLocation objects"""
|
||||||
|
|
||||||
model = EventLocation
|
model = EventLocation
|
||||||
fields = ["name", "icon", "capacity", "conflicts"]
|
fields = ["name", "icon", "capacity", "conflicts"]
|
||||||
|
@ -386,7 +385,7 @@ class EventLocationUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateV
|
||||||
|
|
||||||
|
|
||||||
class EventLocationDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
|
class EventLocationDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
|
||||||
""" This view is used by the Content Team to delete EventLocation objects """
|
"""This view is used by the Content Team to delete EventLocation objects"""
|
||||||
|
|
||||||
model = EventLocation
|
model = EventLocation
|
||||||
template_name = "event_location_delete.html"
|
template_name = "event_location_delete.html"
|
||||||
|
@ -414,7 +413,7 @@ class EventLocationDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteV
|
||||||
|
|
||||||
|
|
||||||
class EventListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class EventListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This view is used by the Content Team to see Event objects. """
|
"""This view is used by the Content Team to see Event objects."""
|
||||||
|
|
||||||
model = Event
|
model = Event
|
||||||
template_name = "event_list_backoffice.html"
|
template_name = "event_list_backoffice.html"
|
||||||
|
@ -431,14 +430,14 @@ class EventListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
|
|
||||||
|
|
||||||
class EventDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
class EventDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
||||||
""" This view is used by the Content Team to see details for Event objects """
|
"""This view is used by the Content Team to see details for Event objects"""
|
||||||
|
|
||||||
model = Event
|
model = Event
|
||||||
template_name = "event_detail_backoffice.html"
|
template_name = "event_detail_backoffice.html"
|
||||||
|
|
||||||
|
|
||||||
class EventUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
class EventUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
||||||
""" This view is used by the Content Team to update Event objects """
|
"""This view is used by the Content Team to update Event objects"""
|
||||||
|
|
||||||
model = Event
|
model = Event
|
||||||
fields = [
|
fields = [
|
||||||
|
@ -460,7 +459,7 @@ class EventUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
||||||
|
|
||||||
|
|
||||||
class EventDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
|
class EventDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
|
||||||
""" This view is used by the Content Team to delete Event objects """
|
"""This view is used by the Content Team to delete Event objects"""
|
||||||
|
|
||||||
model = Event
|
model = Event
|
||||||
template_name = "event_delete.html"
|
template_name = "event_delete.html"
|
||||||
|
@ -721,7 +720,7 @@ class EventSessionDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteVi
|
||||||
context_object_name = "session"
|
context_object_name = "session"
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
""" Show a warning if we have something scheduled in this EventSession """
|
"""Show a warning if we have something scheduled in this EventSession"""
|
||||||
if self.get_object().event_slots.filter(event__isnull=False).exists():
|
if self.get_object().event_slots.filter(event__isnull=False).exists():
|
||||||
messages.warning(
|
messages.warning(
|
||||||
self.request,
|
self.request,
|
||||||
|
@ -749,7 +748,7 @@ class EventSessionDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteVi
|
||||||
|
|
||||||
|
|
||||||
class EventSlotListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
class EventSlotListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
""" This view is used by the Content Team to see EventSlot objects. """
|
"""This view is used by the Content Team to see EventSlot objects."""
|
||||||
|
|
||||||
model = EventSlot
|
model = EventSlot
|
||||||
template_name = "event_slot_list.html"
|
template_name = "event_slot_list.html"
|
||||||
|
@ -766,7 +765,7 @@ class EventSlotListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
|
||||||
|
|
||||||
|
|
||||||
class EventSlotDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
class EventSlotDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView):
|
||||||
""" This view is used by the Content Team to see details for EventSlot objects """
|
"""This view is used by the Content Team to see details for EventSlot objects"""
|
||||||
|
|
||||||
model = EventSlot
|
model = EventSlot
|
||||||
template_name = "event_slot_detail.html"
|
template_name = "event_slot_detail.html"
|
||||||
|
@ -774,7 +773,7 @@ class EventSlotDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView)
|
||||||
|
|
||||||
|
|
||||||
class EventSlotUnscheduleView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
class EventSlotUnscheduleView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
|
||||||
""" This view is used by the Content Team to remove an Event from the schedule/EventSlot """
|
"""This view is used by the Content Team to remove an Event from the schedule/EventSlot"""
|
||||||
|
|
||||||
model = EventSlot
|
model = EventSlot
|
||||||
template_name = "event_slot_unschedule.html"
|
template_name = "event_slot_unschedule.html"
|
||||||
|
@ -802,7 +801,7 @@ class EventSlotUnscheduleView(CampViewMixin, ContentTeamPermissionMixin, UpdateV
|
||||||
|
|
||||||
|
|
||||||
class AutoScheduleManageView(CampViewMixin, ContentTeamPermissionMixin, TemplateView):
|
class AutoScheduleManageView(CampViewMixin, ContentTeamPermissionMixin, TemplateView):
|
||||||
""" Just an index type view with links to the various actions """
|
"""Just an index type view with links to the various actions"""
|
||||||
|
|
||||||
template_name = "autoschedule_index.html"
|
template_name = "autoschedule_index.html"
|
||||||
|
|
||||||
|
@ -810,7 +809,7 @@ class AutoScheduleManageView(CampViewMixin, ContentTeamPermissionMixin, Template
|
||||||
class AutoScheduleCrashCourseView(
|
class AutoScheduleCrashCourseView(
|
||||||
CampViewMixin, ContentTeamPermissionMixin, TemplateView
|
CampViewMixin, ContentTeamPermissionMixin, TemplateView
|
||||||
):
|
):
|
||||||
""" A short crash course on the autoscheduler """
|
"""A short crash course on the autoscheduler"""
|
||||||
|
|
||||||
template_name = "autoschedule_crash_course.html"
|
template_name = "autoschedule_crash_course.html"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from utils.models import CampRelatedModel
|
from utils.models import CampRelatedModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from .models import ProductCategory
|
from .models import ProductCategory
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from channels.auth import AuthMiddlewareStack
|
from channels.auth import AuthMiddlewareStack
|
||||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from program.consumers import ScheduleConsumer
|
from program.consumers import ScheduleConsumer
|
||||||
|
|
||||||
application = ProtocolTypeRouter(
|
application = ProtocolTypeRouter(
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from camps.models import Camp
|
|
||||||
from graphene import ObjectType, Schema, relay
|
from graphene import ObjectType, Schema, relay
|
||||||
from graphene_django import DjangoObjectType
|
from graphene_django import DjangoObjectType
|
||||||
from graphene_django.filter import DjangoFilterConnectionField
|
from graphene_django.filter import DjangoFilterConnectionField
|
||||||
|
|
||||||
|
from camps.models import Camp
|
||||||
from program.schema import ProgramQuery
|
from program.schema import ProgramQuery
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -203,4 +203,4 @@ DATA_UPLOAD_MAX_NUMBER_FIELDS = 5000
|
||||||
ACCOUNT_SIGNUP_FORM_CLASS = "bornhack.forms.AllAuthSignupCaptchaForm"
|
ACCOUNT_SIGNUP_FORM_CLASS = "bornhack.forms.AllAuthSignupCaptchaForm"
|
||||||
|
|
||||||
# django 3.2 https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys
|
# django 3.2 https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
from allauth.account.views import LoginView, LogoutView
|
from allauth.account.views import LoginView, LogoutView
|
||||||
from bar.views import MenuView
|
|
||||||
from camps.views import CampDetailView, CampListView, CampRedirectView
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include
|
from django.conf.urls import include
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
from bar.views import MenuView
|
||||||
|
from camps.views import CampDetailView, CampListView, CampRedirectView
|
||||||
from feedback.views import FeedbackCreate
|
from feedback.views import FeedbackCreate
|
||||||
from info.views import CampInfoView
|
from info.views import CampInfoView
|
||||||
from people.views import PeopleView
|
from people.views import PeopleView
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.models import Camp
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from camps.models import Camp
|
||||||
|
|
||||||
|
|
||||||
class CampViewMixin:
|
class CampViewMixin:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from psycopg2.extras import DateTimeTZRange
|
from psycopg2.extras import DateTimeTZRange
|
||||||
|
|
||||||
from utils.models import CreatedUpdatedModel, UUIDModel
|
from utils.models import CreatedUpdatedModel, UUIDModel
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
@ -135,7 +136,7 @@ class Camp(CreatedUpdatedModel, UUIDModel):
|
||||||
return reverse("camp_detail", kwargs={"camp_slug": self.slug})
|
return reverse("camp_detail", kwargs={"camp_slug": self.slug})
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
""" Make sure the dates make sense - meaning no overlaps and buildup before camp before teardown """
|
"""Make sure the dates make sense - meaning no overlaps and buildup before camp before teardown"""
|
||||||
errors = []
|
errors = []
|
||||||
# check for overlaps buildup vs. camp
|
# check for overlaps buildup vs. camp
|
||||||
if self.buildup.upper > self.camp.lower:
|
if self.buildup.upper > self.camp.lower:
|
||||||
|
@ -258,7 +259,7 @@ class Camp(CreatedUpdatedModel, UUIDModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def event_types(self):
|
def event_types(self):
|
||||||
""" Return all event types with at least one event in this camp """
|
"""Return all event types with at least one event in this camp"""
|
||||||
EventType = apps.get_model("program", "EventType")
|
EventType = apps.get_model("program", "EventType")
|
||||||
return EventType.objects.filter(
|
return EventType.objects.filter(
|
||||||
events__isnull=False, event__track__camp=self
|
events__isnull=False, event__track__camp=self
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from camps.models import Camp
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from camps.models import Camp
|
||||||
|
|
||||||
|
|
||||||
def get_current_camp():
|
def get_current_camp():
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
from utils.email import add_outgoing_email
|
from utils.email import add_outgoing_email
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,9 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"ordering": ["name"],},
|
options={
|
||||||
|
"ordering": ["name"],
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="PosReport",
|
name="PosReport",
|
||||||
|
@ -279,6 +281,8 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"abstract": False,},
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,6 +11,7 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name="posreport", options={"ordering": ["date", "pos"]},
|
name="posreport",
|
||||||
|
options={"ordering": ["date", "pos"]},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.contrib import messages
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
||||||
from utils.slugs import unique_slugify
|
from utils.slugs import unique_slugify
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import magic
|
import magic
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
@ -16,6 +15,8 @@ from django.views.generic import (
|
||||||
TemplateView,
|
TemplateView,
|
||||||
UpdateView,
|
UpdateView,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
from utils.email import add_outgoing_email
|
from utils.email import add_outgoing_email
|
||||||
from utils.mixins import RaisePermissionRequiredMixin
|
from utils.mixins import RaisePermissionRequiredMixin
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
from utils.models import CreatedUpdatedModel
|
from utils.models import CreatedUpdatedModel
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,9 @@ class Migration(migrations.Migration):
|
||||||
models.TextField(help_text="Description of this facility"),
|
models.TextField(help_text="Description of this facility"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"abstract": False,},
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="FacilityQuickFeedback",
|
name="FacilityQuickFeedback",
|
||||||
|
@ -124,7 +126,9 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"unique_together": {("slug", "responsible_team")},},
|
options={
|
||||||
|
"unique_together": {("slug", "responsible_team")},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="FacilityFeedback",
|
name="FacilityFeedback",
|
||||||
|
@ -202,7 +206,9 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"abstract": False,},
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name="facility",
|
model_name="facility",
|
||||||
|
|
|
@ -50,6 +50,8 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"abstract": False,},
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,7 +14,8 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelOptions(
|
migrations.AlterModelOptions(
|
||||||
name="facilityopeninghours", options={"ordering": ["when"]},
|
name="facilityopeninghours",
|
||||||
|
options={"ordering": ["when"]},
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="facility",
|
model_name="facility",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from .models import Facility, FacilityType
|
from .models import Facility, FacilityType
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.contrib.postgres.constraints import ExclusionConstraint
|
||||||
from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
|
from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import reverse
|
from django.shortcuts import reverse
|
||||||
|
|
||||||
from maps.utils import LeafletMarkerChoices
|
from maps.utils import LeafletMarkerChoices
|
||||||
from utils.models import CampRelatedModel, UUIDModel
|
from utils.models import CampRelatedModel, UUIDModel
|
||||||
from utils.slugs import unique_slugify
|
from utils.slugs import unique_slugify
|
||||||
|
@ -111,7 +112,8 @@ class Facility(CampRelatedModel, UUIDModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=100, help_text="Name or description of this facility",
|
max_length=100,
|
||||||
|
help_text="Name or description of this facility",
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.TextField(help_text="Description of this facility")
|
description = models.TextField(help_text="Description of this facility")
|
||||||
|
@ -250,7 +252,8 @@ class FacilityOpeningHours(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
when = DateTimeRangeField(
|
when = DateTimeRangeField(
|
||||||
db_index=True, help_text="The period when this facility is open.",
|
db_index=True,
|
||||||
|
help_text="The period when this facility is open.",
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = models.TextField(
|
notes = models.TextField(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
@ -6,6 +5,8 @@ from django.urls import reverse
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.edit import CreateView
|
from django.views.generic.edit import CreateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from .mixins import FacilityTypeViewMixin, FacilityViewMixin
|
from .mixins import FacilityTypeViewMixin, FacilityViewMixin
|
||||||
from .models import Facility, FacilityFeedback, FacilityType
|
from .models import Facility, FacilityFeedback, FacilityType
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from utils.models import CampRelatedModel, UUIDModel
|
from utils.models import CampRelatedModel, UUIDModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic import CreateView
|
from django.views.generic import CreateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from tokens.models import Token
|
from tokens.models import Token
|
||||||
|
|
||||||
from .models import Feedback
|
from .models import Feedback
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import reversion
|
import reversion
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from utils.models import CampRelatedModel
|
from utils.models import CampRelatedModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from .models import InfoCategory
|
from .models import InfoCategory
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import irc3
|
||||||
from asgiref.sync import sync_to_async
|
from asgiref.sync import sync_to_async
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from ircbot.models import OutgoingIrcMessage
|
from ircbot.models import OutgoingIrcMessage
|
||||||
from teams.models import Team, TeamMember
|
from teams.models import Team, TeamMember
|
||||||
from teams.utils import get_team_from_irc_channel
|
from teams.utils import get_team_from_irc_channel
|
||||||
|
@ -128,8 +129,8 @@ class Plugin(object):
|
||||||
@irc3.extend
|
@irc3.extend
|
||||||
def get_outgoing_messages(self):
|
def get_outgoing_messages(self):
|
||||||
"""
|
"""
|
||||||
This method gets unprocessed OutgoingIrcMessage objects and attempts to send them to
|
This method gets unprocessed OutgoingIrcMessage objects and attempts to send them to
|
||||||
the target channel. Messages are skipped if the bot is not in the channel.
|
the target channel. Messages are skipped if the bot is not in the channel.
|
||||||
"""
|
"""
|
||||||
for msg in OutgoingIrcMessage.objects.filter(processed=False).order_by(
|
for msg in OutgoingIrcMessage.objects.filter(processed=False).order_by(
|
||||||
"created"
|
"created"
|
||||||
|
|
|
@ -9,7 +9,7 @@ logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
def do_work():
|
def do_work():
|
||||||
"""
|
"""
|
||||||
Run irc3 module code, wait for events on IRC and wait for messages in OutgoingIrcMessage
|
Run irc3 module code, wait for events on IRC and wait for messages in OutgoingIrcMessage
|
||||||
"""
|
"""
|
||||||
if hasattr(settings, "IRCBOT_CHANNELS"):
|
if hasattr(settings, "IRCBOT_CHANNELS"):
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from utils.models import CreatedUpdatedModel
|
from utils.models import CreatedUpdatedModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
from utils.models import CreatedUpdatedModel
|
from utils.models import CreatedUpdatedModel
|
||||||
from utils.slugs import unique_slugify
|
from utils.slugs import unique_slugify
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.models import Camp
|
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
|
from camps.models import Camp
|
||||||
|
|
||||||
|
|
||||||
class PeopleView(ListView):
|
class PeopleView(ListView):
|
||||||
template_name = "people.html"
|
template_name = "people.html"
|
||||||
|
|
|
@ -82,6 +82,8 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"unique_together": {("camp", "number")},},
|
options={
|
||||||
|
"unique_together": {("camp", "number")},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from utils.models import CampRelatedModel
|
from utils.models import CampRelatedModel
|
||||||
|
|
||||||
from .dectutils import DectUtils
|
from .dectutils import DectUtils
|
||||||
|
@ -20,7 +21,9 @@ class DectRegistration(CampRelatedModel):
|
||||||
unique_together = [("camp", "number")]
|
unique_together = [("camp", "number")]
|
||||||
|
|
||||||
camp = models.ForeignKey(
|
camp = models.ForeignKey(
|
||||||
"camps.Camp", related_name="dect_registrations", on_delete=models.PROTECT,
|
"camps.Camp",
|
||||||
|
related_name="dect_registrations",
|
||||||
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
|
@ -47,11 +50,14 @@ class DectRegistration(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
activation_code = models.CharField(
|
activation_code = models.CharField(
|
||||||
max_length=10, blank=True, help_text="The 10 digit numeric activation code",
|
max_length=10,
|
||||||
|
blank=True,
|
||||||
|
help_text="The 10 digit numeric activation code",
|
||||||
)
|
)
|
||||||
|
|
||||||
publish_in_phonebook = models.BooleanField(
|
publish_in_phonebook = models.BooleanField(
|
||||||
default=True, help_text="Check to list this registration in the phonebook",
|
default=True,
|
||||||
|
help_text="Check to list this registration in the phonebook",
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
|
@ -3,7 +3,6 @@ import logging
|
||||||
import secrets
|
import secrets
|
||||||
import string
|
import string
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
@ -13,6 +12,8 @@ from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
|
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
|
||||||
from oauth2_provider.views.generic import ProtectedResourceView
|
from oauth2_provider.views.generic import ProtectedResourceView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from utils.mixins import RaisePermissionRequiredMixin, UserIsObjectOwnerMixin
|
from utils.mixins import RaisePermissionRequiredMixin, UserIsObjectOwnerMixin
|
||||||
|
|
||||||
from .mixins import DectRegistrationViewMixin
|
from .mixins import DectRegistrationViewMixin
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from utils.models import CreatedUpdatedModel, UUIDModel
|
from utils.models import CreatedUpdatedModel, UUIDModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ class ProgramConfig(AppConfig):
|
||||||
name = "program"
|
name = "program"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
from .models import Speaker, EventSession
|
from .models import EventSession, Speaker
|
||||||
from .signal_handlers import (
|
from .signal_handlers import (
|
||||||
check_speaker_event_camp_consistency,
|
check_speaker_event_camp_consistency,
|
||||||
event_session_post_save,
|
event_session_post_save,
|
||||||
|
|
|
@ -25,7 +25,7 @@ class AutoScheduler:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, camp):
|
def __init__(self, camp):
|
||||||
""" Get EventTypes, EventSessions and Events, build autoslot and autoevent objects """
|
"""Get EventTypes, EventSessions and Events, build autoslot and autoevent objects"""
|
||||||
self.camp = camp
|
self.camp = camp
|
||||||
|
|
||||||
# Get all EventTypes which support autoscheduling
|
# Get all EventTypes which support autoscheduling
|
||||||
|
@ -62,17 +62,17 @@ class AutoScheduler:
|
||||||
self.autoevents, self.autoeventindex = self.get_autoevents(self.events)
|
self.autoevents, self.autoeventindex = self.get_autoevents(self.events)
|
||||||
|
|
||||||
def get_event_types(self):
|
def get_event_types(self):
|
||||||
""" Return all EventTypes which support autoscheduling """
|
"""Return all EventTypes which support autoscheduling"""
|
||||||
return EventType.objects.filter(support_autoscheduling=True)
|
return EventType.objects.filter(support_autoscheduling=True)
|
||||||
|
|
||||||
def get_event_sessions(self, event_types):
|
def get_event_sessions(self, event_types):
|
||||||
""" Return all EventSessions for these EventTypes """
|
"""Return all EventSessions for these EventTypes"""
|
||||||
return self.camp.event_sessions.filter(
|
return self.camp.event_sessions.filter(
|
||||||
event_type__in=event_types,
|
event_type__in=event_types,
|
||||||
).prefetch_related("event_type", "event_location")
|
).prefetch_related("event_type", "event_location")
|
||||||
|
|
||||||
def get_events(self, event_types):
|
def get_events(self, event_types):
|
||||||
""" Return all Events that need scheduling """
|
"""Return all Events that need scheduling"""
|
||||||
# return all events for these event_types, but..
|
# return all events for these event_types, but..
|
||||||
return self.camp.events.filter(event_type__in=event_types).exclude(
|
return self.camp.events.filter(event_type__in=event_types).exclude(
|
||||||
# exclude Events that have been sceduled already...
|
# exclude Events that have been sceduled already...
|
||||||
|
@ -82,7 +82,7 @@ class AutoScheduler:
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_autoslots(self, event_sessions):
|
def get_autoslots(self, event_sessions):
|
||||||
""" Return a list of autoslots for all slots in all EventSessions """
|
"""Return a list of autoslots for all slots in all EventSessions"""
|
||||||
autoslots = []
|
autoslots = []
|
||||||
# loop over the sessions
|
# loop over the sessions
|
||||||
for session in event_sessions:
|
for session in event_sessions:
|
||||||
|
@ -92,7 +92,7 @@ class AutoScheduler:
|
||||||
return autoslots
|
return autoslots
|
||||||
|
|
||||||
def get_autoevents(self, events):
|
def get_autoevents(self, events):
|
||||||
""" Return a list of resources.Event objects, one for each Event """
|
"""Return a list of resources.Event objects, one for each Event"""
|
||||||
autoevents = []
|
autoevents = []
|
||||||
autoeventindex = {}
|
autoeventindex = {}
|
||||||
eventindex = {}
|
eventindex = {}
|
||||||
|
@ -197,10 +197,10 @@ class AutoScheduler:
|
||||||
return autoevents, autoeventindex
|
return autoevents, autoeventindex
|
||||||
|
|
||||||
def build_current_autoschedule(self):
|
def build_current_autoschedule(self):
|
||||||
""" Build an autoschedule object based on the existing published schedule.
|
"""Build an autoschedule object based on the existing published schedule.
|
||||||
Returns an autoschedule, which is a list of conference_scheduler.resources.ScheduledItem
|
Returns an autoschedule, which is a list of conference_scheduler.resources.ScheduledItem
|
||||||
objects, one for each scheduled Event. This function is useful for creating an "original
|
objects, one for each scheduled Event. This function is useful for creating an "original
|
||||||
schedule" to base a new similar schedule off of. """
|
schedule" to base a new similar schedule off of."""
|
||||||
|
|
||||||
# loop over scheduled events and create a ScheduledItem object for each
|
# loop over scheduled events and create a ScheduledItem object for each
|
||||||
autoschedule = []
|
autoschedule = []
|
||||||
|
@ -244,8 +244,8 @@ class AutoScheduler:
|
||||||
return autoschedule
|
return autoschedule
|
||||||
|
|
||||||
def calculate_autoschedule(self, original_schedule=None):
|
def calculate_autoschedule(self, original_schedule=None):
|
||||||
""" Calculate autoschedule based on self.autoevents and self.autoslots,
|
"""Calculate autoschedule based on self.autoevents and self.autoslots,
|
||||||
optionally using original_schedule to minimise changes """
|
optionally using original_schedule to minimise changes"""
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
kwargs["events"] = self.autoevents
|
kwargs["events"] = self.autoevents
|
||||||
kwargs["slots"] = self.autoslots
|
kwargs["slots"] = self.autoslots
|
||||||
|
@ -264,8 +264,8 @@ class AutoScheduler:
|
||||||
return autoschedule
|
return autoschedule
|
||||||
|
|
||||||
def calculate_similar_autoschedule(self, original_schedule=None):
|
def calculate_similar_autoschedule(self, original_schedule=None):
|
||||||
""" Convenience method for creating similar schedules. If original_schedule
|
"""Convenience method for creating similar schedules. If original_schedule
|
||||||
is omitted the new schedule is based on the current schedule instead """
|
is omitted the new schedule is based on the current schedule instead"""
|
||||||
|
|
||||||
if not original_schedule:
|
if not original_schedule:
|
||||||
# we do not have an original_schedule, use current EventInstances
|
# we do not have an original_schedule, use current EventInstances
|
||||||
|
@ -277,7 +277,7 @@ class AutoScheduler:
|
||||||
return autoschedule, diff
|
return autoschedule, diff
|
||||||
|
|
||||||
def apply(self, autoschedule):
|
def apply(self, autoschedule):
|
||||||
""" Apply an autoschedule by creating EventInstance objects to match it """
|
"""Apply an autoschedule by creating EventInstance objects to match it"""
|
||||||
|
|
||||||
# "The Clean Slate protocol sir?" - delete any existing autoscheduled Events
|
# "The Clean Slate protocol sir?" - delete any existing autoscheduled Events
|
||||||
# TODO: investigate how this affects the FRAB XML export (for which we added a UUID on
|
# TODO: investigate how this affects the FRAB XML export (for which we added a UUID on
|
||||||
|
@ -320,7 +320,10 @@ class AutoScheduler:
|
||||||
This method returns a dict of Event differences and Slot differences between
|
This method returns a dict of Event differences and Slot differences between
|
||||||
the two schedules.
|
the two schedules.
|
||||||
"""
|
"""
|
||||||
slot_diff = scheduler.slot_schedule_difference(original_schedule, new_schedule,)
|
slot_diff = scheduler.slot_schedule_difference(
|
||||||
|
original_schedule,
|
||||||
|
new_schedule,
|
||||||
|
)
|
||||||
|
|
||||||
slot_output = []
|
slot_output = []
|
||||||
for item in slot_diff:
|
for item in slot_diff:
|
||||||
|
@ -347,7 +350,8 @@ class AutoScheduler:
|
||||||
|
|
||||||
# then get a list of differences per event
|
# then get a list of differences per event
|
||||||
event_diff = scheduler.event_schedule_difference(
|
event_diff = scheduler.event_schedule_difference(
|
||||||
original_schedule, new_schedule,
|
original_schedule,
|
||||||
|
new_schedule,
|
||||||
)
|
)
|
||||||
event_output = []
|
event_output = []
|
||||||
# loop over the differences and build the dict
|
# loop over the differences and build the dict
|
||||||
|
@ -357,7 +361,11 @@ class AutoScheduler:
|
||||||
except self.camp.events.DoesNotExist:
|
except self.camp.events.DoesNotExist:
|
||||||
event = item.event.name
|
event = item.event.name
|
||||||
event_output.append(
|
event_output.append(
|
||||||
{"event": event, "old": {}, "new": {},}
|
{
|
||||||
|
"event": event,
|
||||||
|
"old": {},
|
||||||
|
"new": {},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
# do we have an old slot for this event?
|
# do we have an old slot for this event?
|
||||||
if item.old_slot:
|
if item.old_slot:
|
||||||
|
@ -376,7 +384,7 @@ class AutoScheduler:
|
||||||
return {"event_diffs": event_output, "slot_diffs": slot_output}
|
return {"event_diffs": event_output, "slot_diffs": slot_output}
|
||||||
|
|
||||||
def is_valid(self, autoschedule, return_violations=False):
|
def is_valid(self, autoschedule, return_violations=False):
|
||||||
""" Check if a schedule is valid, optionally returning a list of violations if invalid """
|
"""Check if a schedule is valid, optionally returning a list of violations if invalid"""
|
||||||
valid = is_valid_schedule(
|
valid = is_valid_schedule(
|
||||||
autoschedule, slots=self.autoslots, events=self.autoevents
|
autoschedule, slots=self.autoslots, events=self.autoevents
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.models import Camp
|
|
||||||
from channels.generic.websocket import JsonWebsocketConsumer
|
from channels.generic.websocket import JsonWebsocketConsumer
|
||||||
|
|
||||||
|
from camps.models import Camp
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
Event,
|
Event,
|
||||||
EventInstance,
|
EventInstance,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
from utils.email import add_outgoing_email
|
from utils.email import add_outgoing_email
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,8 @@ class SpeakerProposalForm(forms.ModelForm):
|
||||||
|
|
||||||
# only show events from this camp
|
# only show events from this camp
|
||||||
self.fields["event_conflicts"].queryset = Event.objects.filter(
|
self.fields["event_conflicts"].queryset = Event.objects.filter(
|
||||||
track__camp=camp, event_type__support_speaker_event_conflicts=True,
|
track__camp=camp,
|
||||||
|
event_type__support_speaker_event_conflicts=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
if matrix:
|
if matrix:
|
||||||
|
@ -265,9 +266,9 @@ class EventProposalForm(forms.ModelForm):
|
||||||
]
|
]
|
||||||
|
|
||||||
def clean_duration(self):
|
def clean_duration(self):
|
||||||
""" Make sure duration has been specified, and make sure it is not too long """
|
"""Make sure duration has been specified, and make sure it is not too long"""
|
||||||
if not self.cleaned_data["duration"]:
|
if not self.cleaned_data["duration"]:
|
||||||
raise forms.ValidationError(f"Please specify a duration.")
|
raise forms.ValidationError("Please specify a duration.")
|
||||||
if (
|
if (
|
||||||
self.event_type.event_duration_minutes
|
self.event_type.event_duration_minutes
|
||||||
and self.cleaned_data["duration"] > self.event_type.event_duration_minutes
|
and self.cleaned_data["duration"] > self.event_type.event_duration_minutes
|
||||||
|
|
|
@ -2,10 +2,11 @@ import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from camps.utils import get_current_camp
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from camps.utils import get_current_camp
|
||||||
from ircbot.models import OutgoingIrcMessage
|
from ircbot.models import OutgoingIrcMessage
|
||||||
from program.models import EventInstance
|
from program.models import EventInstance
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
# Generated by Django 1.10.5 on 2017-02-18 11:43
|
# Generated by Django 1.10.5 on 2017-02-18 11:43
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import program.models
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import program.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,11 @@ from __future__ import unicode_literals
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import program.models
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import program.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,11 @@ from __future__ import unicode_literals
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import program.models
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import program.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
# Generated by Django 1.10.5 on 2017-03-14 19:12
|
# Generated by Django 1.10.5 on 2017-03-14 19:12
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import program.models
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import program.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
# Generated by Django 1.10.5 on 2017-03-15 23:04
|
# Generated by Django 1.10.5 on 2017-03-15 23:04
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import program.models
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import program.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.AlterUniqueTogether(
|
migrations.AlterUniqueTogether(
|
||||||
name="eventfeedback", unique_together={("user", "event")},
|
name="eventfeedback",
|
||||||
|
unique_together={("user", "event")},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,6 +11,8 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RenameField(
|
migrations.RenameField(
|
||||||
model_name="eventfeedback", old_name="feedback", new_name="comment",
|
model_name="eventfeedback",
|
||||||
|
old_name="feedback",
|
||||||
|
new_name="comment",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -51,7 +51,9 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"ordering": ["when", "event_type", "event_location"],},
|
options={
|
||||||
|
"ordering": ["when", "event_type", "event_location"],
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="EventSlot",
|
name="EventSlot",
|
||||||
|
@ -81,7 +83,9 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={"ordering": ["when"],},
|
options={
|
||||||
|
"ordering": ["when"],
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="SpeakerAvailability",
|
name="SpeakerAvailability",
|
||||||
|
@ -128,7 +132,9 @@ class Migration(migrations.Migration):
|
||||||
("created", models.DateTimeField(auto_now_add=True)),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
("updated", models.DateTimeField(auto_now=True)),
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
],
|
],
|
||||||
options={"abstract": False,},
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="SpeakerProposalAvailability",
|
name="SpeakerProposalAvailability",
|
||||||
|
@ -175,7 +181,9 @@ class Migration(migrations.Migration):
|
||||||
("created", models.DateTimeField(auto_now_add=True)),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
("updated", models.DateTimeField(auto_now=True)),
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
],
|
],
|
||||||
options={"abstract": False,},
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name="event",
|
model_name="event",
|
||||||
|
|
|
@ -32,13 +32,19 @@ class Migration(migrations.Migration):
|
||||||
new_name="speaker_proposal",
|
new_name="speaker_proposal",
|
||||||
),
|
),
|
||||||
migrations.RenameField(
|
migrations.RenameField(
|
||||||
model_name="url", old_name="eventproposal", new_name="event_proposal",
|
model_name="url",
|
||||||
|
old_name="eventproposal",
|
||||||
|
new_name="event_proposal",
|
||||||
),
|
),
|
||||||
migrations.RenameField(
|
migrations.RenameField(
|
||||||
model_name="url", old_name="speakerproposal", new_name="speaker_proposal",
|
model_name="url",
|
||||||
|
old_name="speakerproposal",
|
||||||
|
new_name="speaker_proposal",
|
||||||
),
|
),
|
||||||
migrations.RenameField(
|
migrations.RenameField(
|
||||||
model_name="url", old_name="urltype", new_name="url_type",
|
model_name="url",
|
||||||
|
old_name="urltype",
|
||||||
|
new_name="url_type",
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="event",
|
model_name="event",
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
import django.contrib.postgres.constraints
|
import django.contrib.postgres.constraints
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import utils.database
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import utils.database
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
@ -13,13 +14,21 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RemoveField(model_name="speakereventconflict", name="events",),
|
|
||||||
migrations.RemoveField(model_name="speakereventconflict", name="speaker",),
|
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name="speakerproposaleventconflict", name="events",
|
model_name="speakereventconflict",
|
||||||
|
name="events",
|
||||||
),
|
),
|
||||||
migrations.RemoveField(
|
migrations.RemoveField(
|
||||||
model_name="speakerproposaleventconflict", name="speaker_proposal",
|
model_name="speakereventconflict",
|
||||||
|
name="speaker",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="speakerproposaleventconflict",
|
||||||
|
name="events",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="speakerproposaleventconflict",
|
||||||
|
name="speaker_proposal",
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name="eventtype",
|
model_name="eventtype",
|
||||||
|
@ -110,6 +119,10 @@ class Migration(migrations.Migration):
|
||||||
name="prevent_speaker_proposal_availability_adjacent_mergeable",
|
name="prevent_speaker_proposal_availability_adjacent_mergeable",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
migrations.DeleteModel(name="SpeakerEventConflict",),
|
migrations.DeleteModel(
|
||||||
migrations.DeleteModel(name="SpeakerProposalEventConflict",),
|
name="SpeakerEventConflict",
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name="SpeakerProposalEventConflict",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from program.utils import (
|
from program.utils import (
|
||||||
add_existing_availability_to_matrix,
|
add_existing_availability_to_matrix,
|
||||||
get_speaker_availability_form_matrix,
|
get_speaker_availability_form_matrix,
|
||||||
|
@ -141,7 +142,9 @@ class EventFeedbackViewMixin(EventViewMixin):
|
||||||
def setup(self, *args, **kwargs):
|
def setup(self, *args, **kwargs):
|
||||||
super().setup(*args, **kwargs)
|
super().setup(*args, **kwargs)
|
||||||
self.event_feedback = get_object_or_404(
|
self.event_feedback = get_object_or_404(
|
||||||
models.EventFeedback, event=self.event, user=self.request.user,
|
models.EventFeedback,
|
||||||
|
event=self.event,
|
||||||
|
user=self.request.user,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
@ -157,7 +160,7 @@ class AvailabilityMatrixViewMixin(CampViewMixin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def setup(self, *args, **kwargs):
|
def setup(self, *args, **kwargs):
|
||||||
""" Get the availability matrix"""
|
"""Get the availability matrix"""
|
||||||
super().setup(*args, **kwargs)
|
super().setup(*args, **kwargs)
|
||||||
# do we have an Event or an EventProposal?
|
# do we have an Event or an EventProposal?
|
||||||
if hasattr(self.get_object(), "events"):
|
if hasattr(self.get_object(), "events"):
|
||||||
|
@ -177,13 +180,13 @@ class AvailabilityMatrixViewMixin(CampViewMixin):
|
||||||
add_existing_availability_to_matrix(self.matrix, self.get_object())
|
add_existing_availability_to_matrix(self.matrix, self.get_object())
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
""" Add the matrix to form kwargs, only used if the view has a form """
|
"""Add the matrix to form kwargs, only used if the view has a form"""
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs["matrix"] = self.matrix
|
kwargs["matrix"] = self.matrix
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_initial(self, *args, **kwargs):
|
def get_initial(self, *args, **kwargs):
|
||||||
""" Populate the speaker_availability checkboxes, only used if the view has a form """
|
"""Populate the speaker_availability checkboxes, only used if the view has a form"""
|
||||||
initial = super().get_initial(*args, **kwargs)
|
initial = super().get_initial(*args, **kwargs)
|
||||||
|
|
||||||
# add initial checkbox states
|
# add initial checkbox states
|
||||||
|
@ -202,7 +205,7 @@ class AvailabilityMatrixViewMixin(CampViewMixin):
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
""" Add the matrix to context """
|
"""Add the matrix to context"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["matrix"] = self.matrix
|
context["matrix"] = self.matrix
|
||||||
return context
|
return context
|
||||||
|
|
|
@ -18,6 +18,7 @@ from django.utils import timezone
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from psycopg2.extras import DateTimeTZRange
|
from psycopg2.extras import DateTimeTZRange
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from utils.database import CastToInteger
|
from utils.database import CastToInteger
|
||||||
from utils.models import (
|
from utils.models import (
|
||||||
CampRelatedModel,
|
CampRelatedModel,
|
||||||
|
@ -119,7 +120,7 @@ class Url(CampRelatedModel):
|
||||||
return self.url
|
return self.url
|
||||||
|
|
||||||
def clean_fk(self):
|
def clean_fk(self):
|
||||||
""" Make sure we have exactly one FK """
|
"""Make sure we have exactly one FK"""
|
||||||
fks = 0
|
fks = 0
|
||||||
if self.speaker_proposal:
|
if self.speaker_proposal:
|
||||||
fks += 1
|
fks += 1
|
||||||
|
@ -192,10 +193,10 @@ class Availability(CampRelatedModel, UUIDModel):
|
||||||
|
|
||||||
|
|
||||||
class SpeakerProposalAvailability(Availability):
|
class SpeakerProposalAvailability(Availability):
|
||||||
""" Availability info for SpeakerProposal objects """
|
"""Availability info for SpeakerProposal objects"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
""" Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability """
|
"""Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability"""
|
||||||
|
|
||||||
constraints = [
|
constraints = [
|
||||||
# we do not want overlapping ranges
|
# we do not want overlapping ranges
|
||||||
|
@ -247,10 +248,10 @@ class SpeakerProposalAvailability(Availability):
|
||||||
|
|
||||||
|
|
||||||
class SpeakerAvailability(Availability):
|
class SpeakerAvailability(Availability):
|
||||||
""" Availability info for Speaker objects """
|
"""Availability info for Speaker objects"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
""" Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability """
|
"""Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability"""
|
||||||
|
|
||||||
constraints = [
|
constraints = [
|
||||||
# we do not want overlapping ranges
|
# we do not want overlapping ranges
|
||||||
|
@ -290,7 +291,9 @@ class SpeakerAvailability(Availability):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
# this should be an ExclusionConstraint but the boolean condition isn't conditioning :/
|
# this should be an ExclusionConstraint but the boolean condition isn't conditioning :/
|
||||||
if SpeakerAvailability.objects.filter(
|
if SpeakerAvailability.objects.filter(
|
||||||
speaker=self.speaker, when__adjacent_to=self.when, available=self.available,
|
speaker=self.speaker,
|
||||||
|
when__adjacent_to=self.when,
|
||||||
|
available=self.available,
|
||||||
).exists():
|
).exists():
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
"An adjacent SpeakerAvailability object for this Speaker already exists with the same value for available, cannot save()"
|
"An adjacent SpeakerAvailability object for this Speaker already exists with the same value for available, cannot save()"
|
||||||
|
@ -302,8 +305,8 @@ class SpeakerAvailability(Availability):
|
||||||
|
|
||||||
class UserSubmittedModel(CampRelatedModel):
|
class UserSubmittedModel(CampRelatedModel):
|
||||||
"""
|
"""
|
||||||
An abstract model containing the stuff that is shared
|
An abstract model containing the stuff that is shared
|
||||||
between the SpeakerProposal and EventProposal models.
|
between the SpeakerProposal and EventProposal models.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -359,7 +362,7 @@ class UserSubmittedModel(CampRelatedModel):
|
||||||
|
|
||||||
|
|
||||||
class SpeakerProposal(UserSubmittedModel):
|
class SpeakerProposal(UserSubmittedModel):
|
||||||
""" A speaker proposal """
|
"""A speaker proposal"""
|
||||||
|
|
||||||
camp = models.ForeignKey(
|
camp = models.ForeignKey(
|
||||||
"camps.Camp",
|
"camps.Camp",
|
||||||
|
@ -410,7 +413,7 @@ class SpeakerProposal(UserSubmittedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
def mark_as_approved(self, request=None):
|
def mark_as_approved(self, request=None):
|
||||||
""" Marks a SpeakerProposal as approved, including creating/updating the related Speaker object """
|
"""Marks a SpeakerProposal as approved, including creating/updating the related Speaker object"""
|
||||||
speaker_proposalmodel = apps.get_model("program", "SpeakerProposal")
|
speaker_proposalmodel = apps.get_model("program", "SpeakerProposal")
|
||||||
# create a Speaker if we don't have one
|
# create a Speaker if we don't have one
|
||||||
if not hasattr(self, "speaker"):
|
if not hasattr(self, "speaker"):
|
||||||
|
@ -470,14 +473,14 @@ class SpeakerProposal(UserSubmittedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def event_types(self):
|
def event_types(self):
|
||||||
""" Return a queryset of the EventType objects for the EventProposals """
|
"""Return a queryset of the EventType objects for the EventProposals"""
|
||||||
return EventType.objects.filter(
|
return EventType.objects.filter(
|
||||||
id__in=self.event_proposals.all().values_list("event_type", flat=True)
|
id__in=self.event_proposals.all().values_list("event_type", flat=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
""" Convenience method to return the proper host_title """
|
"""Convenience method to return the proper host_title"""
|
||||||
if self.event_proposals.values_list("event_type").distinct().count() != 1:
|
if self.event_proposals.values_list("event_type").distinct().count() != 1:
|
||||||
# we have no events, or events of different eventtypes, use generic title
|
# we have no events, or events of different eventtypes, use generic title
|
||||||
return "Person"
|
return "Person"
|
||||||
|
@ -486,7 +489,7 @@ class SpeakerProposal(UserSubmittedModel):
|
||||||
|
|
||||||
|
|
||||||
class EventProposal(UserSubmittedModel):
|
class EventProposal(UserSubmittedModel):
|
||||||
""" An event proposal """
|
"""An event proposal"""
|
||||||
|
|
||||||
track = models.ForeignKey(
|
track = models.ForeignKey(
|
||||||
"program.EventTrack",
|
"program.EventTrack",
|
||||||
|
@ -538,7 +541,10 @@ class EventProposal(UserSubmittedModel):
|
||||||
help_text="Will you be using the provided speaker laptop?", default=True
|
help_text="Will you be using the provided speaker laptop?", default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager(through=UUIDTaggedItem, blank=True,)
|
tags = TaggableManager(
|
||||||
|
through=UUIDTaggedItem,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def camp(self):
|
def camp(self):
|
||||||
|
@ -618,7 +624,7 @@ class EventProposal(UserSubmittedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_be_approved(self):
|
def can_be_approved(self):
|
||||||
""" We cannot approve an EventProposal until all SpeakerProposals are approved """
|
"""We cannot approve an EventProposal until all SpeakerProposals are approved"""
|
||||||
if self.speakers.exclude(proposal_status="approved").exists():
|
if self.speakers.exclude(proposal_status="approved").exists():
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
@ -629,7 +635,7 @@ class EventProposal(UserSubmittedModel):
|
||||||
|
|
||||||
|
|
||||||
class EventTrack(CampRelatedModel):
|
class EventTrack(CampRelatedModel):
|
||||||
""" All events belong to a track. Administration of a track can be delegated to one or more users. """
|
"""All events belong to a track. Administration of a track can be delegated to one or more users."""
|
||||||
|
|
||||||
name = models.CharField(max_length=100, help_text="The name of this Track")
|
name = models.CharField(max_length=100, help_text="The name of this Track")
|
||||||
|
|
||||||
|
@ -660,7 +666,7 @@ class EventTrack(CampRelatedModel):
|
||||||
|
|
||||||
|
|
||||||
class EventLocation(CampRelatedModel):
|
class EventLocation(CampRelatedModel):
|
||||||
""" The places where stuff happens """
|
"""The places where stuff happens"""
|
||||||
|
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
@ -693,7 +699,7 @@ class EventLocation(CampRelatedModel):
|
||||||
unique_together = (("camp", "slug"), ("camp", "name"))
|
unique_together = (("camp", "slug"), ("camp", "name"))
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
""" Create a slug """
|
"""Create a slug"""
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = unique_slugify(
|
self.slug = unique_slugify(
|
||||||
self.name,
|
self.name,
|
||||||
|
@ -715,11 +721,11 @@ class EventLocation(CampRelatedModel):
|
||||||
return self.camp.event_slots.filter(event_session__event_location=self)
|
return self.camp.event_slots.filter(event_session__event_location=self)
|
||||||
|
|
||||||
def scheduled_event_slots(self):
|
def scheduled_event_slots(self):
|
||||||
""" Returns a QuerySet of all EventSlots scheduled in this EventLocation """
|
"""Returns a QuerySet of all EventSlots scheduled in this EventLocation"""
|
||||||
return self.event_slots.filter(event__isnull=False)
|
return self.event_slots.filter(event__isnull=False)
|
||||||
|
|
||||||
def is_available(self, when, ignore_event_slot_ids=[]):
|
def is_available(self, when, ignore_event_slot_ids=[]):
|
||||||
""" A location is available if nothing is scheduled in it at that time """
|
"""A location is available if nothing is scheduled in it at that time"""
|
||||||
if (
|
if (
|
||||||
self.event_slots.filter(event__isnull=False, when__overlap=when)
|
self.event_slots.filter(event__isnull=False, when__overlap=when)
|
||||||
.exclude(pk__in=ignore_event_slot_ids)
|
.exclude(pk__in=ignore_event_slot_ids)
|
||||||
|
@ -732,7 +738,7 @@ class EventLocation(CampRelatedModel):
|
||||||
|
|
||||||
|
|
||||||
class EventType(CreatedUpdatedModel):
|
class EventType(CreatedUpdatedModel):
|
||||||
""" Every event needs to have a type. """
|
"""Every event needs to have a type."""
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=100, unique=True, help_text="The name of this event type"
|
max_length=100, unique=True, help_text="The name of this event type"
|
||||||
|
@ -785,7 +791,8 @@ class EventType(CreatedUpdatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
support_autoscheduling = models.BooleanField(
|
support_autoscheduling = models.BooleanField(
|
||||||
default=False, help_text="Check to enable this EventType in the autoscheduler",
|
default=False,
|
||||||
|
help_text="Check to enable this EventType in the autoscheduler",
|
||||||
)
|
)
|
||||||
|
|
||||||
support_speaker_event_conflicts = models.BooleanField(
|
support_speaker_event_conflicts = models.BooleanField(
|
||||||
|
@ -812,7 +819,7 @@ class EventType(CreatedUpdatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def duration(self):
|
def duration(self):
|
||||||
""" Just return a timedelta of the lenght of this Session """
|
"""Just return a timedelta of the lenght of this Session"""
|
||||||
return timedelta(minutes=self.event_duration_minutes)
|
return timedelta(minutes=self.event_duration_minutes)
|
||||||
|
|
||||||
def icon_html(self):
|
def icon_html(self):
|
||||||
|
@ -891,7 +898,8 @@ class EventSession(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.TextField(
|
description = models.TextField(
|
||||||
blank=True, help_text="Description of this session (optional).",
|
blank=True,
|
||||||
|
help_text="Description of this session (optional).",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -904,12 +912,12 @@ class EventSession(CampRelatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def duration(self):
|
def duration(self):
|
||||||
""" Just return a timedelta of the lenght of this Session """
|
"""Just return a timedelta of the lenght of this Session"""
|
||||||
return self.when.upper - self.when.lower
|
return self.when.upper - self.when.lower
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def free_time(self):
|
def free_time(self):
|
||||||
""" Returns a timedelta of the free time in this Session. """
|
"""Returns a timedelta of the free time in this Session."""
|
||||||
return self.duration - timedelta(
|
return self.duration - timedelta(
|
||||||
minutes=self.event_duration_minutes * self.get_unavailable_slots().count()
|
minutes=self.event_duration_minutes * self.get_unavailable_slots().count()
|
||||||
)
|
)
|
||||||
|
@ -954,15 +962,16 @@ class EventSession(CampRelatedModel):
|
||||||
return self.event_slots.filter(availablefilter).exclude(excludefilter)
|
return self.event_slots.filter(availablefilter).exclude(excludefilter)
|
||||||
|
|
||||||
def get_unavailable_slots(self, count_autoscheduled_as_free=False, bounds="[)"):
|
def get_unavailable_slots(self, count_autoscheduled_as_free=False, bounds="[)"):
|
||||||
""" Return a list of slots that are not available for some reason """
|
"""Return a list of slots that are not available for some reason"""
|
||||||
return self.event_slots.exclude(
|
return self.event_slots.exclude(
|
||||||
id__in=self.get_available_slots(
|
id__in=self.get_available_slots(
|
||||||
count_autoscheduled_as_free=count_autoscheduled_as_free, bounds=bounds,
|
count_autoscheduled_as_free=count_autoscheduled_as_free,
|
||||||
|
bounds=bounds,
|
||||||
).values_list("id", flat=True),
|
).values_list("id", flat=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_slot_times(self, bounds="[)"):
|
def get_slot_times(self, bounds="[)"):
|
||||||
""" Return a list of the DateTimeTZRanges we want EventSlots to exists for """
|
"""Return a list of the DateTimeTZRanges we want EventSlots to exists for"""
|
||||||
slots = []
|
slots = []
|
||||||
period = self.when
|
period = self.when
|
||||||
duration = timedelta(minutes=self.event_duration_minutes)
|
duration = timedelta(minutes=self.event_duration_minutes)
|
||||||
|
@ -985,7 +994,7 @@ class EventSession(CampRelatedModel):
|
||||||
return slots
|
return slots
|
||||||
|
|
||||||
def fixup_event_slots(self):
|
def fixup_event_slots(self):
|
||||||
""" This method takes care of creating and deleting EventSlots when the EventSession is created, updated or deleted """
|
"""This method takes care of creating and deleting EventSlots when the EventSession is created, updated or deleted"""
|
||||||
# get a set of DateTimeTZRange objects representing the EventSlots we need
|
# get a set of DateTimeTZRange objects representing the EventSlots we need
|
||||||
needed_slot_times = set(self.get_slot_times(bounds="[)"))
|
needed_slot_times = set(self.get_slot_times(bounds="[)"))
|
||||||
|
|
||||||
|
@ -1036,7 +1045,9 @@ class EventSlot(CampRelatedModel):
|
||||||
help_text="The EventSession this EventSlot belongs to",
|
help_text="The EventSession this EventSlot belongs to",
|
||||||
)
|
)
|
||||||
|
|
||||||
when = DateTimeRangeField(help_text="Start and end time of this slot",)
|
when = DateTimeRangeField(
|
||||||
|
help_text="Start and end time of this slot",
|
||||||
|
)
|
||||||
|
|
||||||
event = models.ForeignKey(
|
event = models.ForeignKey(
|
||||||
"program.Event",
|
"program.Event",
|
||||||
|
@ -1064,7 +1075,7 @@ class EventSlot(CampRelatedModel):
|
||||||
return f"{self.when} ({self.event_session.event_location.name}, {self.event_session.event_type})"
|
return f"{self.when} ({self.event_session.event_location.name}, {self.event_session.event_type})"
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
""" Validate EventSlot length, time, and autoscheduled status"""
|
"""Validate EventSlot length, time, and autoscheduled status"""
|
||||||
if self.when.upper - self.when.lower != timedelta(
|
if self.when.upper - self.when.lower != timedelta(
|
||||||
minutes=self.event_session.event_duration_minutes
|
minutes=self.event_session.event_duration_minutes
|
||||||
):
|
):
|
||||||
|
@ -1087,7 +1098,7 @@ class EventSlot(CampRelatedModel):
|
||||||
self.clean_location()
|
self.clean_location()
|
||||||
|
|
||||||
def get_autoscheduler_slot(self):
|
def get_autoscheduler_slot(self):
|
||||||
""" Return a conference_scheduler.resources.Slot object matching this EventSlot """
|
"""Return a conference_scheduler.resources.Slot object matching this EventSlot"""
|
||||||
return resources.Slot(
|
return resources.Slot(
|
||||||
venue=self.event_session.event_location.id,
|
venue=self.event_session.event_location.id,
|
||||||
starts_at=self.when.lower,
|
starts_at=self.when.lower,
|
||||||
|
@ -1105,7 +1116,7 @@ class EventSlot(CampRelatedModel):
|
||||||
return self.event_session.event_location
|
return self.event_session.event_location
|
||||||
|
|
||||||
def clean_speakers(self):
|
def clean_speakers(self):
|
||||||
""" Check if all speakers are available """
|
"""Check if all speakers are available"""
|
||||||
if self.event:
|
if self.event:
|
||||||
for speaker in self.event.speakers.all():
|
for speaker in self.event.speakers.all():
|
||||||
if not speaker.is_available(
|
if not speaker.is_available(
|
||||||
|
@ -1116,7 +1127,7 @@ class EventSlot(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean_location(self):
|
def clean_location(self):
|
||||||
""" Make sure the location is available """
|
"""Make sure the location is available"""
|
||||||
if self.event:
|
if self.event:
|
||||||
if not self.event_location.is_available(
|
if not self.event_location.is_available(
|
||||||
when=self.when, ignore_event_slot_ids=[self.pk]
|
when=self.when, ignore_event_slot_ids=[self.pk]
|
||||||
|
@ -1126,14 +1137,14 @@ class EventSlot(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
def unschedule(self):
|
def unschedule(self):
|
||||||
""" Clear the Event FK and autoscheduled status, removing the Event from the schedule """
|
"""Clear the Event FK and autoscheduled status, removing the Event from the schedule"""
|
||||||
self.event = None
|
self.event = None
|
||||||
self.autoscheduled = None
|
self.autoscheduled = None
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def overflow(self):
|
def overflow(self):
|
||||||
""" If we have more demand than capacity return the overflow """
|
"""If we have more demand than capacity return the overflow"""
|
||||||
if self.event and self.event.demand > self.event_location.capacity:
|
if self.event and self.event.demand > self.event_location.capacity:
|
||||||
return (self.event_location.capacity - self.event.demand) * -1
|
return (self.event_location.capacity - self.event.demand) * -1
|
||||||
else:
|
else:
|
||||||
|
@ -1207,7 +1218,7 @@ class EventSlot(CampRelatedModel):
|
||||||
|
|
||||||
|
|
||||||
class Event(CampRelatedModel):
|
class Event(CampRelatedModel):
|
||||||
""" Something that is on the program one or more times. """
|
"""Something that is on the program one or more times."""
|
||||||
|
|
||||||
uuid = models.UUIDField(
|
uuid = models.UUIDField(
|
||||||
default=uuid.uuid4,
|
default=uuid.uuid4,
|
||||||
|
@ -1278,7 +1289,7 @@ class Event(CampRelatedModel):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
""" Create a slug and get duration """
|
"""Create a slug and get duration"""
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = unique_slugify(
|
self.slug = unique_slugify(
|
||||||
self.title,
|
self.title,
|
||||||
|
@ -1336,7 +1347,7 @@ class Event(CampRelatedModel):
|
||||||
|
|
||||||
|
|
||||||
class EventInstance(CampRelatedModel):
|
class EventInstance(CampRelatedModel):
|
||||||
""" The old way of scheduling events. Model to be deleted after prod data migration """
|
"""The old way of scheduling events. Model to be deleted after prod data migration"""
|
||||||
|
|
||||||
uuid = models.UUIDField(
|
uuid = models.UUIDField(
|
||||||
default=uuid.uuid4,
|
default=uuid.uuid4,
|
||||||
|
@ -1362,7 +1373,8 @@ class EventInstance(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
autoscheduled = models.BooleanField(
|
autoscheduled = models.BooleanField(
|
||||||
default=False, help_text="True if this was created by the autoscheduler.",
|
default=False,
|
||||||
|
help_text="True if this was created by the autoscheduler.",
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1382,7 +1394,7 @@ class EventInstance(CampRelatedModel):
|
||||||
return "%s (%s)" % (self.event, self.when)
|
return "%s (%s)" % (self.event, self.when)
|
||||||
|
|
||||||
def clean_speakers(self):
|
def clean_speakers(self):
|
||||||
""" Check if all speakers are available """
|
"""Check if all speakers are available"""
|
||||||
for speaker in self.event.speakers.all():
|
for speaker in self.event.speakers.all():
|
||||||
if not speaker.is_available(
|
if not speaker.is_available(
|
||||||
when=self.event_slot.when, ignore_eventinstances=[self.pk]
|
when=self.event_slot.when, ignore_eventinstances=[self.pk]
|
||||||
|
@ -1392,7 +1404,7 @@ class EventInstance(CampRelatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, clean_speakers=True, **kwargs):
|
def save(self, *args, clean_speakers=True, **kwargs):
|
||||||
""" Validate speakers (unless we are asked not to) """
|
"""Validate speakers (unless we are asked not to)"""
|
||||||
if "commit" not in kwargs or kwargs["commit"]:
|
if "commit" not in kwargs or kwargs["commit"]:
|
||||||
# we are saving for real
|
# we are saving for real
|
||||||
if clean_speakers:
|
if clean_speakers:
|
||||||
|
@ -1408,11 +1420,11 @@ class EventInstance(CampRelatedModel):
|
||||||
@property
|
@property
|
||||||
def schedule_date(self):
|
def schedule_date(self):
|
||||||
"""
|
"""
|
||||||
Returns the schedule date of this eventinstance. Schedule date is determined by substracting
|
Returns the schedule date of this eventinstance. Schedule date is determined by substracting
|
||||||
settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS from the eventinstance start time. This means that if
|
settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS from the eventinstance start time. This means that if
|
||||||
an event is scheduled for 00:30 wednesday evening (technically thursday) then the date
|
an event is scheduled for 00:30 wednesday evening (technically thursday) then the date
|
||||||
after substracting 5 hours would be wednesdays date, not thursdays
|
after substracting 5 hours would be wednesdays date, not thursdays
|
||||||
(given settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS=5)
|
(given settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS=5)
|
||||||
"""
|
"""
|
||||||
return (
|
return (
|
||||||
self.when.lower - timedelta(hours=settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS)
|
self.when.lower - timedelta(hours=settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS)
|
||||||
|
@ -1420,7 +1432,7 @@ class EventInstance(CampRelatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def timeslots(self):
|
def timeslots(self):
|
||||||
""" Find the number of timeslots this eventinstance takes up """
|
"""Find the number of timeslots this eventinstance takes up"""
|
||||||
seconds = (self.when.upper - self.when.lower).seconds
|
seconds = (self.when.upper - self.when.lower).seconds
|
||||||
minutes = seconds / 60
|
minutes = seconds / 60
|
||||||
return minutes / settings.SCHEDULE_TIMESLOT_LENGTH_MINUTES
|
return minutes / settings.SCHEDULE_TIMESLOT_LENGTH_MINUTES
|
||||||
|
@ -1470,7 +1482,7 @@ class EventInstance(CampRelatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def duration(self):
|
def duration(self):
|
||||||
""" Return a timedelta of the lenght of this EventInstance """
|
"""Return a timedelta of the lenght of this EventInstance"""
|
||||||
return self.when.upper - self.when.lower
|
return self.when.upper - self.when.lower
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1482,7 +1494,7 @@ class EventInstance(CampRelatedModel):
|
||||||
|
|
||||||
|
|
||||||
class Speaker(CampRelatedModel):
|
class Speaker(CampRelatedModel):
|
||||||
""" A Person (co)anchoring one or more events on a camp. """
|
"""A Person (co)anchoring one or more events on a camp."""
|
||||||
|
|
||||||
name = models.CharField(max_length=150, help_text="Name or alias of the speaker")
|
name = models.CharField(max_length=150, help_text="Name or alias of the speaker")
|
||||||
|
|
||||||
|
@ -1559,9 +1571,9 @@ class Speaker(CampRelatedModel):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def is_available(self, when, ignore_event_slot_ids=[]):
|
def is_available(self, when, ignore_event_slot_ids=[]):
|
||||||
""" A speaker is available if the person has positive availability for the period and
|
"""A speaker is available if the person has positive availability for the period and
|
||||||
if the speaker is not in another event at the time, or if the person has not submitted
|
if the speaker is not in another event at the time, or if the person has not submitted
|
||||||
any availability at all """
|
any availability at all"""
|
||||||
if not self.availabilities.exists():
|
if not self.availabilities.exists():
|
||||||
# we have no availability at all for this speaker, assume they are available
|
# we have no availability at all for this speaker, assume they are available
|
||||||
return True
|
return True
|
||||||
|
@ -1585,12 +1597,12 @@ class Speaker(CampRelatedModel):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def scheduled_event_slots(self):
|
def scheduled_event_slots(self):
|
||||||
""" Returns a QuerySet of all EventSlots scheduled for this speaker """
|
"""Returns a QuerySet of all EventSlots scheduled for this speaker"""
|
||||||
return self.camp.event_slots.filter(event__speakers=self)
|
return self.camp.event_slots.filter(event__speakers=self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self):
|
def title(self):
|
||||||
""" Convenience method to return the proper host_title """
|
"""Convenience method to return the proper host_title"""
|
||||||
if self.events.values_list("event_type").distinct().count() > 1:
|
if self.events.values_list("event_type").distinct().count() > 1:
|
||||||
# we have different eventtypes, use generic title
|
# we have different eventtypes, use generic title
|
||||||
return "Person"
|
return "Person"
|
||||||
|
@ -1638,7 +1650,8 @@ class EventFeedback(CampRelatedModel, UUIDModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
expectations_fulfilled = models.BooleanField(
|
expectations_fulfilled = models.BooleanField(
|
||||||
choices=YESNO_CHOICES, help_text="Did the event live up to your expectations?",
|
choices=YESNO_CHOICES,
|
||||||
|
help_text="Did the event live up to your expectations?",
|
||||||
)
|
)
|
||||||
|
|
||||||
attend_speaker_again = models.BooleanField(
|
attend_speaker_again = models.BooleanField(
|
||||||
|
@ -1649,7 +1662,8 @@ class EventFeedback(CampRelatedModel, UUIDModel):
|
||||||
RATING_CHOICES = [(n, f"{n}") for n in range(0, 6)]
|
RATING_CHOICES = [(n, f"{n}") for n in range(0, 6)]
|
||||||
|
|
||||||
rating = models.IntegerField(
|
rating = models.IntegerField(
|
||||||
choices=RATING_CHOICES, help_text="Rating/Score (5 is best)",
|
choices=RATING_CHOICES,
|
||||||
|
help_text="Rating/Score (5 is best)",
|
||||||
)
|
)
|
||||||
|
|
||||||
comment = models.TextField(blank=True, help_text="Any other comments or feedback?")
|
comment = models.TextField(blank=True, help_text="Any other comments or feedback?")
|
||||||
|
@ -1682,15 +1696,12 @@ class CustomUrlStorage(FileSystemStorage):
|
||||||
Can be removed when we clean up old migrations at some point
|
Can be removed when we clean up old migrations at some point
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_speaker_picture_upload_path():
|
def get_speaker_picture_upload_path():
|
||||||
"""
|
"""
|
||||||
Must exist because it is mentioned in old migrations.
|
Must exist because it is mentioned in old migrations.
|
||||||
Can be removed when we clean up old migrations at some point
|
Can be removed when we clean up old migrations at some point
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_speakerproposal_picture_upload_path():
|
def get_speakerproposal_picture_upload_path():
|
||||||
|
@ -1698,7 +1709,6 @@ def get_speakerproposal_picture_upload_path():
|
||||||
Must exist because it is mentioned in old migrations.
|
Must exist because it is mentioned in old migrations.
|
||||||
Can be removed when we clean up old migrations at some point
|
Can be removed when we clean up old migrations at some point
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_speakersubmission_picture_upload_path():
|
def get_speakersubmission_picture_upload_path():
|
||||||
|
@ -1706,4 +1716,3 @@ def get_speakersubmission_picture_upload_path():
|
||||||
Must exist because it is mentioned in old migrations.
|
Must exist because it is mentioned in old migrations.
|
||||||
Can be removed when we clean up old migrations at some point
|
Can be removed when we clean up old migrations at some point
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
|
|
@ -50,7 +50,8 @@ class MultiForm(object):
|
||||||
# Some things, such as the WizardView expect these to exist.
|
# Some things, such as the WizardView expect these to exist.
|
||||||
self.data, self.files = data, files
|
self.data, self.files = data, files
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
data=data, files=files,
|
data=data,
|
||||||
|
files=files,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initials = kwargs.pop("initial", None)
|
self.initials = kwargs.pop("initial", None)
|
||||||
|
@ -74,7 +75,8 @@ class MultiForm(object):
|
||||||
else:
|
else:
|
||||||
prefix = "{0}__{1}".format(key, prefix)
|
prefix = "{0}__{1}".format(key, prefix)
|
||||||
fkwargs.update(
|
fkwargs.update(
|
||||||
initial=self.initials.get(key), prefix=prefix,
|
initial=self.initials.get(key),
|
||||||
|
prefix=prefix,
|
||||||
)
|
)
|
||||||
return args, fkwargs
|
return args, fkwargs
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
def check_speaker_event_camp_consistency(sender, instance, **kwargs):
|
def check_speaker_event_camp_consistency(sender, instance, **kwargs):
|
||||||
if kwargs["action"] == "pre_add":
|
if kwargs["action"] == "pre_add":
|
||||||
from program.models import Speaker, Event
|
from program.models import Event, Speaker
|
||||||
|
|
||||||
if isinstance(instance, Event):
|
if isinstance(instance, Event):
|
||||||
# loop over speakers being added to this event
|
# loop over speakers being added to this event
|
||||||
|
@ -35,5 +35,5 @@ def check_speaker_event_camp_consistency(sender, instance, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def event_session_post_save(sender, instance, created, **kwargs):
|
def event_session_post_save(sender, instance, created, **kwargs):
|
||||||
""" Make sure we have the number of EventSlots we need to have, adjust if not """
|
"""Make sure we have the number of EventSlots we need to have, adjust if not"""
|
||||||
instance.fixup_event_slots()
|
instance.fixup_event_slots()
|
||||||
|
|
|
@ -9,13 +9,13 @@ register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
def render_datetime(datetime):
|
def render_datetime(datetime):
|
||||||
""" Renders a datetime in the users timezone """
|
"""Renders a datetime in the users timezone"""
|
||||||
t = Template("{{ datetime }}")
|
t = Template("{{ datetime }}")
|
||||||
return t.render(Context({"datetime": datetime}))
|
return t.render(Context({"datetime": datetime}))
|
||||||
|
|
||||||
|
|
||||||
def render_datetimetzrange(datetimetzrange):
|
def render_datetimetzrange(datetimetzrange):
|
||||||
""" Renders a datetimetzrange as 14:00-16:00 in the users timezone """
|
"""Renders a datetimetzrange as 14:00-16:00 in the users timezone"""
|
||||||
return f"{render_datetime(datetimetzrange.lower.time())}-{render_datetime(datetimetzrange.upper.time())} ({datetimetzrange.lower.tzname()})"
|
return f"{render_datetime(datetimetzrange.lower.time())}-{render_datetime(datetimetzrange.upper.time())} ({datetimetzrange.lower.tzname()})"
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ def availabilitytable(matrix, form=None):
|
||||||
# start bulding the output
|
# start bulding the output
|
||||||
output = "<div class='form-group'>"
|
output = "<div class='form-group'>"
|
||||||
output += "<table class='table table-hover table-condensed table-bordered table-responsive' style='margin-bottom: .25em;'><thead>"
|
output += "<table class='table table-hover table-condensed table-bordered table-responsive' style='margin-bottom: .25em;'><thead>"
|
||||||
output += f"<tr><th class='text-nowrap'>Speaker Availability</th>"
|
output += "<tr><th class='text-nowrap'>Speaker Availability</th>"
|
||||||
|
|
||||||
# to build the <thead> for this table we need to loop over the days (dates)
|
# to build the <thead> for this table we need to loop over the days (dates)
|
||||||
# in the matrix and create a column for each
|
# in the matrix and create a column for each
|
||||||
|
|
|
@ -93,7 +93,11 @@ def get_speaker_availability_form_matrix(sessions):
|
||||||
# pass a list of dicts instead of the queryset to avoid one million lookups
|
# pass a list of dicts instead of the queryset to avoid one million lookups
|
||||||
for et in event_types:
|
for et in event_types:
|
||||||
matrix[day][daychunk]["event_types"].append(
|
matrix[day][daychunk]["event_types"].append(
|
||||||
{"name": et.name, "icon": et.icon, "color": et.color,}
|
{
|
||||||
|
"name": et.name,
|
||||||
|
"icon": et.icon,
|
||||||
|
"color": et.color,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
matrix[day][daychunk]["initial"] = None
|
matrix[day][daychunk]["initial"] = None
|
||||||
else:
|
else:
|
||||||
|
@ -205,7 +209,9 @@ def save_speaker_availability(form, obj):
|
||||||
else:
|
else:
|
||||||
# "available" changed or daychunk is not adjacent to formerchunk
|
# "available" changed or daychunk is not adjacent to formerchunk
|
||||||
AvailabilityModel.objects.create(
|
AvailabilityModel.objects.create(
|
||||||
when=formerchunk, available=formeravailable, **kwargs,
|
when=formerchunk,
|
||||||
|
available=formeravailable,
|
||||||
|
**kwargs,
|
||||||
)
|
)
|
||||||
# and remember the current chunk for next iteration
|
# and remember the current chunk for next iteration
|
||||||
formerchunk = daychunk
|
formerchunk = daychunk
|
||||||
|
|
|
@ -2,7 +2,6 @@ import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import icalendar
|
import icalendar
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
|
@ -15,6 +14,8 @@ from django.utils.decorators import method_decorator
|
||||||
from django.views.generic import DetailView, ListView, TemplateView, View
|
from django.views.generic import DetailView, ListView, TemplateView, View
|
||||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from utils.middleware import RedirectException
|
from utils.middleware import RedirectException
|
||||||
from utils.mixins import UserIsObjectOwnerMixin
|
from utils.mixins import UserIsObjectOwnerMixin
|
||||||
|
|
||||||
|
@ -82,7 +83,8 @@ class ICSView(CampViewMixin, View):
|
||||||
query_kwargs["event__video_recording"] = False
|
query_kwargs["event__video_recording"] = False
|
||||||
|
|
||||||
event_slots = models.EventSlot.objects.filter(
|
event_slots = models.EventSlot.objects.filter(
|
||||||
event__track__camp=self.camp, **query_kwargs,
|
event__track__camp=self.camp,
|
||||||
|
**query_kwargs,
|
||||||
).prefetch_related("event", "event_session__event_location")
|
).prefetch_related("event", "event_session__event_location")
|
||||||
|
|
||||||
cal = icalendar.Calendar()
|
cal = icalendar.Calendar()
|
||||||
|
@ -109,7 +111,7 @@ class ProposalListView(LoginRequiredMixin, CampViewMixin, ListView):
|
||||||
context_object_name = "speaker_proposal_list"
|
context_object_name = "speaker_proposal_list"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
""" Only show speaker proposals for the current user """
|
"""Only show speaker proposals for the current user"""
|
||||||
return (
|
return (
|
||||||
super()
|
super()
|
||||||
.get_queryset()
|
.get_queryset()
|
||||||
|
@ -123,7 +125,7 @@ class ProposalListView(LoginRequiredMixin, CampViewMixin, ListView):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
""" Add event_proposals to the context """
|
"""Add event_proposals to the context"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["event_proposal_list"] = models.EventProposal.objects.filter(
|
context["event_proposal_list"] = models.EventProposal.objects.filter(
|
||||||
track__camp=self.camp, user=self.request.user
|
track__camp=self.camp, user=self.request.user
|
||||||
|
@ -143,14 +145,14 @@ class SpeakerProposalCreateView(
|
||||||
EnsureCFPOpenMixin,
|
EnsureCFPOpenMixin,
|
||||||
CreateView,
|
CreateView,
|
||||||
):
|
):
|
||||||
""" This view allows a user to create a new SpeakerProposal linked to an existing EventProposal """
|
"""This view allows a user to create a new SpeakerProposal linked to an existing EventProposal"""
|
||||||
|
|
||||||
model = models.SpeakerProposal
|
model = models.SpeakerProposal
|
||||||
template_name = "speaker_proposal_form.html"
|
template_name = "speaker_proposal_form.html"
|
||||||
form_class = SpeakerProposalForm
|
form_class = SpeakerProposalForm
|
||||||
|
|
||||||
def setup(self, *args, **kwargs):
|
def setup(self, *args, **kwargs):
|
||||||
""" Get the event_proposal object and speaker availability matrix"""
|
"""Get the event_proposal object and speaker availability matrix"""
|
||||||
super().setup(*args, **kwargs)
|
super().setup(*args, **kwargs)
|
||||||
""" Get the event_proposal and availability matrix """
|
""" Get the event_proposal and availability matrix """
|
||||||
self.event_proposal = get_object_or_404(
|
self.event_proposal = get_object_or_404(
|
||||||
|
@ -166,7 +168,7 @@ class SpeakerProposalCreateView(
|
||||||
return reverse("program:proposal_list", kwargs={"camp_slug": self.camp.slug})
|
return reverse("program:proposal_list", kwargs={"camp_slug": self.camp.slug})
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
""" Set camp and event_type for the form """
|
"""Set camp and event_type for the form"""
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
{
|
{
|
||||||
|
@ -178,7 +180,7 @@ class SpeakerProposalCreateView(
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_initial(self, *args, **kwargs):
|
def get_initial(self, *args, **kwargs):
|
||||||
""" Populate the speaker_availability checkboxes """
|
"""Populate the speaker_availability checkboxes"""
|
||||||
initial = super().get_initial(*args, **kwargs)
|
initial = super().get_initial(*args, **kwargs)
|
||||||
# loop over dates in the matrix
|
# loop over dates in the matrix
|
||||||
for date in self.matrix.keys():
|
for date in self.matrix.keys():
|
||||||
|
@ -196,7 +198,7 @@ class SpeakerProposalCreateView(
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
""" Set user and camp before saving, then save availability """
|
"""Set user and camp before saving, then save availability"""
|
||||||
speaker_proposal = form.save(commit=False)
|
speaker_proposal = form.save(commit=False)
|
||||||
speaker_proposal.user = self.request.user
|
speaker_proposal.user = self.request.user
|
||||||
speaker_proposal.camp = self.camp
|
speaker_proposal.camp = self.camp
|
||||||
|
@ -234,20 +236,20 @@ class SpeakerProposalUpdateView(
|
||||||
EnsureCFPOpenMixin,
|
EnsureCFPOpenMixin,
|
||||||
UpdateView,
|
UpdateView,
|
||||||
):
|
):
|
||||||
""" This view allows a user to update an existing SpeakerProposal. """
|
"""This view allows a user to update an existing SpeakerProposal."""
|
||||||
|
|
||||||
model = models.SpeakerProposal
|
model = models.SpeakerProposal
|
||||||
template_name = "speaker_proposal_form.html"
|
template_name = "speaker_proposal_form.html"
|
||||||
form_class = SpeakerProposalForm
|
form_class = SpeakerProposalForm
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
""" Prefetch availabilities for this SpeakerProposal """
|
"""Prefetch availabilities for this SpeakerProposal"""
|
||||||
qs = self.model.objects.filter(pk=self.kwargs.get(self.pk_url_kwarg))
|
qs = self.model.objects.filter(pk=self.kwargs.get(self.pk_url_kwarg))
|
||||||
qs = qs.prefetch_related("availabilities")
|
qs = qs.prefetch_related("availabilities")
|
||||||
return qs.get()
|
return qs.get()
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
""" Set camp, matrix and event_type for the form """
|
"""Set camp, matrix and event_type for the form"""
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
|
|
||||||
# get all event types this SpeakerProposal is involved in
|
# get all event types this SpeakerProposal is involved in
|
||||||
|
@ -269,7 +271,7 @@ class SpeakerProposalUpdateView(
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
""" Change status and save availability """
|
"""Change status and save availability"""
|
||||||
speaker_proposal = form.save(commit=False)
|
speaker_proposal = form.save(commit=False)
|
||||||
|
|
||||||
# set proposal status to pending
|
# set proposal status to pending
|
||||||
|
@ -295,7 +297,7 @@ class SpeakerProposalUpdateView(
|
||||||
# message user and redirect
|
# message user and redirect
|
||||||
messages.info(
|
messages.info(
|
||||||
self.request,
|
self.request,
|
||||||
f"Changes to your proposal is now pending approval by the content team.",
|
"Changes to your proposal is now pending approval by the content team.",
|
||||||
)
|
)
|
||||||
return redirect(
|
return redirect(
|
||||||
reverse(
|
reverse(
|
||||||
|
@ -335,7 +337,7 @@ class SpeakerProposalDeleteView(
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
""" Delete availabilities before deleting the proposal """
|
"""Delete availabilities before deleting the proposal"""
|
||||||
self.get_object().availabilities.all().delete()
|
self.get_object().availabilities.all().delete()
|
||||||
return super().delete(*args, **kwargs)
|
return super().delete(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -377,18 +379,18 @@ class EventProposalTypeSelectView(
|
||||||
context_object_name = "event_type_list"
|
context_object_name = "event_type_list"
|
||||||
|
|
||||||
def setup(self, *args, **kwargs):
|
def setup(self, *args, **kwargs):
|
||||||
""" Get the speaker_proposal object """
|
"""Get the speaker_proposal object"""
|
||||||
super().setup(*args, **kwargs)
|
super().setup(*args, **kwargs)
|
||||||
self.speaker = get_object_or_404(
|
self.speaker = get_object_or_404(
|
||||||
models.SpeakerProposal, pk=kwargs["speaker_uuid"]
|
models.SpeakerProposal, pk=kwargs["speaker_uuid"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
""" We only allow submissions of events with EventTypes where public=True """
|
"""We only allow submissions of events with EventTypes where public=True"""
|
||||||
return super().get_queryset().filter(public=True)
|
return super().get_queryset().filter(public=True)
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
""" Make speaker_proposal object available in template """
|
"""Make speaker_proposal object available in template"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["speaker"] = self.speaker
|
context["speaker"] = self.speaker
|
||||||
return context
|
return context
|
||||||
|
@ -410,18 +412,18 @@ class EventProposalSelectPersonView(
|
||||||
context_object_name = "speaker_proposal_list"
|
context_object_name = "speaker_proposal_list"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
""" Get EventProposal from url kwargs """
|
"""Get EventProposal from url kwargs"""
|
||||||
self.event_proposal = get_object_or_404(
|
self.event_proposal = get_object_or_404(
|
||||||
models.EventProposal, pk=kwargs["event_uuid"], user=request.user
|
models.EventProposal, pk=kwargs["event_uuid"], user=request.user
|
||||||
)
|
)
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
""" Filter out any speaker_proposals already added to this event_proposal """
|
"""Filter out any speaker_proposals already added to this event_proposal"""
|
||||||
return self.event_proposal.get_available_speaker_proposals().all()
|
return self.event_proposal.get_available_speaker_proposals().all()
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
""" Make event_proposal object available in template """
|
"""Make event_proposal object available in template"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["event_proposal"] = self.event_proposal
|
context["event_proposal"] = self.event_proposal
|
||||||
return context
|
return context
|
||||||
|
@ -445,14 +447,14 @@ class EventProposalAddPersonView(
|
||||||
context_object_name = "event_proposal"
|
context_object_name = "event_proposal"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
""" Get the speaker_proposal object """
|
"""Get the speaker_proposal object"""
|
||||||
self.speaker_proposal = get_object_or_404(
|
self.speaker_proposal = get_object_or_404(
|
||||||
models.SpeakerProposal, pk=kwargs["speaker_uuid"], user=request.user
|
models.SpeakerProposal, pk=kwargs["speaker_uuid"], user=request.user
|
||||||
)
|
)
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
""" Make speaker_proposal object available in template """
|
"""Make speaker_proposal object available in template"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["speaker_proposal"] = self.speaker_proposal
|
context["speaker_proposal"] = self.speaker_proposal
|
||||||
return context
|
return context
|
||||||
|
@ -488,7 +490,7 @@ class EventProposalRemovePersonView(
|
||||||
pk_url_kwarg = "event_uuid"
|
pk_url_kwarg = "event_uuid"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
""" Get the speaker_proposal object and check a few things """
|
"""Get the speaker_proposal object and check a few things"""
|
||||||
# get the speaker_proposal object from URL kwargs
|
# get the speaker_proposal object from URL kwargs
|
||||||
self.speaker_proposal = get_object_or_404(
|
self.speaker_proposal = get_object_or_404(
|
||||||
models.SpeakerProposal, pk=kwargs["speaker_uuid"], user=request.user
|
models.SpeakerProposal, pk=kwargs["speaker_uuid"], user=request.user
|
||||||
|
@ -496,13 +498,13 @@ class EventProposalRemovePersonView(
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
""" Make speaker_proposal object available in template """
|
"""Make speaker_proposal object available in template"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["speaker_proposal"] = self.speaker_proposal
|
context["speaker_proposal"] = self.speaker_proposal
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
""" Remove the speaker from the event """
|
"""Remove the speaker from the event"""
|
||||||
if self.speaker_proposal not in self.get_object().speakers.all():
|
if self.speaker_proposal not in self.get_object().speakers.all():
|
||||||
# this speaker is not associated with this event
|
# this speaker is not associated with this event
|
||||||
raise Http404
|
raise Http404
|
||||||
|
@ -545,7 +547,7 @@ class EventProposalCreateView(
|
||||||
form_class = EventProposalForm
|
form_class = EventProposalForm
|
||||||
|
|
||||||
def setup(self, *args, **kwargs):
|
def setup(self, *args, **kwargs):
|
||||||
""" Get the speaker_proposal object """
|
"""Get the speaker_proposal object"""
|
||||||
super().setup(*args, **kwargs)
|
super().setup(*args, **kwargs)
|
||||||
self.speaker_proposal = get_object_or_404(
|
self.speaker_proposal = get_object_or_404(
|
||||||
models.SpeakerProposal, pk=self.kwargs["speaker_uuid"]
|
models.SpeakerProposal, pk=self.kwargs["speaker_uuid"]
|
||||||
|
@ -555,7 +557,7 @@ class EventProposalCreateView(
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
""" Make speaker_proposal object available in template """
|
"""Make speaker_proposal object available in template"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["speaker"] = self.speaker_proposal
|
context["speaker"] = self.speaker_proposal
|
||||||
context["event_type"] = self.event_type
|
context["event_type"] = self.event_type
|
||||||
|
@ -570,7 +572,7 @@ class EventProposalCreateView(
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
""" set camp and user for this event_proposal, save slideurl """
|
"""set camp and user for this event_proposal, save slideurl"""
|
||||||
event_proposal = form.save(commit=False)
|
event_proposal = form.save(commit=False)
|
||||||
event_proposal.user = self.request.user
|
event_proposal.user = self.request.user
|
||||||
event_proposal.event_type = self.event_type
|
event_proposal.event_type = self.event_type
|
||||||
|
@ -630,7 +632,7 @@ class EventProposalUpdateView(
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
""" Make speaker_proposal and event_type objects available in the template """
|
"""Make speaker_proposal and event_type objects available in the template"""
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["event_type"] = self.get_object().event_type
|
context["event_type"] = self.get_object().event_type
|
||||||
return context
|
return context
|
||||||
|
@ -711,7 +713,7 @@ class CombinedProposalTypeSelectView(LoginRequiredMixin, CampViewMixin, ListView
|
||||||
template_name = "event_type_select.html"
|
template_name = "event_type_select.html"
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
""" We only allow submissions of events with EventTypes where public=True """
|
"""We only allow submissions of events with EventTypes where public=True"""
|
||||||
return super().get_queryset().filter(public=True)
|
return super().get_queryset().filter(public=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -727,14 +729,14 @@ class CombinedProposalPersonSelectView(LoginRequiredMixin, CampViewMixin, ListVi
|
||||||
context_object_name = "speaker_proposal_list"
|
context_object_name = "speaker_proposal_list"
|
||||||
|
|
||||||
def setup(self, *args, **kwargs):
|
def setup(self, *args, **kwargs):
|
||||||
""" Check that we have a valid EventType """
|
"""Check that we have a valid EventType"""
|
||||||
super().setup(*args, **kwargs)
|
super().setup(*args, **kwargs)
|
||||||
self.event_type = get_object_or_404(
|
self.event_type = get_object_or_404(
|
||||||
models.EventType, slug=self.kwargs["event_type_slug"]
|
models.EventType, slug=self.kwargs["event_type_slug"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_queryset(self, **kwargs):
|
def get_queryset(self, **kwargs):
|
||||||
""" only show speaker proposals for the current user """
|
"""only show speaker proposals for the current user"""
|
||||||
return super().get_queryset().filter(user=self.request.user)
|
return super().get_queryset().filter(user=self.request.user)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
@ -746,7 +748,7 @@ class CombinedProposalPersonSelectView(LoginRequiredMixin, CampViewMixin, ListVi
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
""" If we don't have any existing SpeakerProposals just redirect directly to the combined submit view """
|
"""If we don't have any existing SpeakerProposals just redirect directly to the combined submit view"""
|
||||||
if not self.get_queryset().exists():
|
if not self.get_queryset().exists():
|
||||||
return redirect(
|
return redirect(
|
||||||
reverse_lazy(
|
reverse_lazy(
|
||||||
|
@ -1004,7 +1006,9 @@ class FrabXmlView(CampViewMixin, View):
|
||||||
qs = (
|
qs = (
|
||||||
models.EventSlot.objects.filter(event__track__camp=self.camp)
|
models.EventSlot.objects.filter(event__track__camp=self.camp)
|
||||||
.prefetch_related(
|
.prefetch_related(
|
||||||
"event__urls", "event__speakers", "event_session__event_location",
|
"event__urls",
|
||||||
|
"event__speakers",
|
||||||
|
"event_session__event_location",
|
||||||
)
|
)
|
||||||
.order_by("when", "event_session__event_location")
|
.order_by("when", "event_session__event_location")
|
||||||
)
|
)
|
||||||
|
@ -1327,7 +1331,7 @@ class UrlDeleteView(
|
||||||
|
|
||||||
|
|
||||||
class FeedbackRedirectView(LoginRequiredMixin, EventViewMixin, DetailView):
|
class FeedbackRedirectView(LoginRequiredMixin, EventViewMixin, DetailView):
|
||||||
""" Redirect to the appropriate view """
|
"""Redirect to the appropriate view"""
|
||||||
|
|
||||||
model = models.Event
|
model = models.Event
|
||||||
slug_url_kwarg = "event_slug"
|
slug_url_kwarg = "event_slug"
|
||||||
|
|
|
@ -11,6 +11,8 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RenameField(
|
migrations.RenameField(
|
||||||
model_name="ride", old_name="location", new_name="from_location",
|
model_name="ride",
|
||||||
|
old_name="location",
|
||||||
|
new_name="from_location",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from utils.models import CampRelatedModel, UUIDModel
|
from utils.models import CampRelatedModel, UUIDModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
@ -11,6 +10,8 @@ from django.views.generic import (
|
||||||
ListView,
|
ListView,
|
||||||
UpdateView,
|
UpdateView,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
from utils.email import add_outgoing_email
|
from utils.email import add_outgoing_email
|
||||||
from utils.mixins import UserIsObjectOwnerMixin
|
from utils.mixins import UserIsObjectOwnerMixin
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from tickets.admin import ShopTicketInline
|
from tickets.admin import ShopTicketInline
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from vendor.coinify.coinify_api import CoinifyAPI
|
from vendor.coinify.coinify_api import CoinifyAPI
|
||||||
|
|
||||||
from .models import CoinifyAPICallback, CoinifyAPIInvoice, CoinifyAPIRequest
|
from .models import CoinifyAPICallback, CoinifyAPIInvoice, CoinifyAPIRequest
|
||||||
|
|
|
@ -2,6 +2,7 @@ import factory
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from factory.django import DjangoModelFactory
|
from factory.django import DjangoModelFactory
|
||||||
from psycopg2.extras import DateTimeTZRange
|
from psycopg2.extras import DateTimeTZRange
|
||||||
|
|
||||||
from utils.factories import UserFactory
|
from utils.factories import UserFactory
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import modelformset_factory
|
from django.forms import modelformset_factory
|
||||||
|
|
||||||
from shop.models import OrderProductRelation
|
from shop.models import OrderProductRelation
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import logging
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from shop.email import add_creditnote_email, add_invoice_email
|
from shop.email import add_creditnote_email, add_invoice_email
|
||||||
from shop.models import CreditNote, CustomOrder, Invoice, Order
|
from shop.models import CreditNote, CustomOrder, Invoice, Order
|
||||||
from utils.pdf import generate_pdf_letter
|
from utils.pdf import generate_pdf_letter
|
||||||
|
@ -13,10 +14,10 @@ logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
def do_work():
|
def do_work():
|
||||||
"""
|
"""
|
||||||
The invoice worker creates Invoice objects for shop orders and
|
The invoice worker creates Invoice objects for shop orders and
|
||||||
for custom orders. It also generates PDF files for Invoice objects
|
for custom orders. It also generates PDF files for Invoice objects
|
||||||
that have no PDF. It also emails invoices for shop orders.
|
that have no PDF. It also emails invoices for shop orders.
|
||||||
It also generates proforma invoices for all closed orders.
|
It also generates proforma invoices for all closed orders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# check if we need to generate any proforma invoices for shop orders
|
# check if we need to generate any proforma invoices for shop orders
|
||||||
|
|
|
@ -11,7 +11,9 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="coinifyapicallback", name="headers", field=models.JSONField(),
|
model_name="coinifyapicallback",
|
||||||
|
name="headers",
|
||||||
|
field=models.JSONField(),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="coinifyapicallback",
|
model_name="coinifyapicallback",
|
||||||
|
@ -24,13 +26,19 @@ class Migration(migrations.Migration):
|
||||||
field=models.JSONField(),
|
field=models.JSONField(),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="coinifyapirequest", name="payload", field=models.JSONField(),
|
model_name="coinifyapirequest",
|
||||||
|
name="payload",
|
||||||
|
field=models.JSONField(),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="coinifyapirequest", name="response", field=models.JSONField(),
|
model_name="coinifyapirequest",
|
||||||
|
name="response",
|
||||||
|
field=models.JSONField(),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="epaycallback", name="payload", field=models.JSONField(),
|
model_name="epaycallback",
|
||||||
|
name="payload",
|
||||||
|
field=models.JSONField(),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name="order",
|
model_name="order",
|
||||||
|
|
|
@ -13,6 +13,7 @@ from django.utils import timezone
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from unidecode import unidecode
|
from unidecode import unidecode
|
||||||
|
|
||||||
from utils.models import CreatedUpdatedModel, UUIDModel
|
from utils.models import CreatedUpdatedModel, UUIDModel
|
||||||
from utils.slugs import unique_slugify
|
from utils.slugs import unique_slugify
|
||||||
|
|
||||||
|
@ -160,7 +161,7 @@ class Order(CreatedUpdatedModel):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_coinify_callback_url(self, request):
|
def get_coinify_callback_url(self, request):
|
||||||
""" Check settings for an alternative COINIFY_CALLBACK_HOSTNAME otherwise use the one from the request """
|
"""Check settings for an alternative COINIFY_CALLBACK_HOSTNAME otherwise use the one from the request"""
|
||||||
if (
|
if (
|
||||||
hasattr(settings, "COINIFY_CALLBACK_HOSTNAME")
|
hasattr(settings, "COINIFY_CALLBACK_HOSTNAME")
|
||||||
and settings.COINIFY_CALLBACK_HOSTNAME
|
and settings.COINIFY_CALLBACK_HOSTNAME
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from psycopg2.extras import DateTimeTZRange
|
from psycopg2.extras import DateTimeTZRange
|
||||||
|
|
||||||
from shop.forms import OrderProductRelationForm
|
from shop.forms import OrderProductRelationForm
|
||||||
from tickets.factories import TicketTypeFactory
|
from tickets.factories import TicketTypeFactory
|
||||||
from tickets.models import ShopTicket
|
from tickets.models import ShopTicket
|
||||||
|
@ -11,16 +12,16 @@ from .factories import OrderFactory, OrderProductRelationFactory, ProductFactory
|
||||||
|
|
||||||
|
|
||||||
class ProductAvailabilityTest(TestCase):
|
class ProductAvailabilityTest(TestCase):
|
||||||
""" Test logic about availability of products. """
|
"""Test logic about availability of products."""
|
||||||
|
|
||||||
def test_product_available_by_stock(self):
|
def test_product_available_by_stock(self):
|
||||||
""" If no orders have been made, the product is still available. """
|
"""If no orders have been made, the product is still available."""
|
||||||
product = ProductFactory(stock_amount=10)
|
product = ProductFactory(stock_amount=10)
|
||||||
self.assertEqual(product.left_in_stock, 10)
|
self.assertEqual(product.left_in_stock, 10)
|
||||||
self.assertTrue(product.is_available())
|
self.assertTrue(product.is_available())
|
||||||
|
|
||||||
def test_product_not_available_by_stock(self):
|
def test_product_not_available_by_stock(self):
|
||||||
""" If max orders have been made, the product is NOT available. """
|
"""If max orders have been made, the product is NOT available."""
|
||||||
product = ProductFactory(stock_amount=2)
|
product = ProductFactory(stock_amount=2)
|
||||||
|
|
||||||
OrderProductRelationFactory(product=product, order__open=None)
|
OrderProductRelationFactory(product=product, order__open=None)
|
||||||
|
@ -39,14 +40,14 @@ class ProductAvailabilityTest(TestCase):
|
||||||
self.assertTrue(product.is_available())
|
self.assertTrue(product.is_available())
|
||||||
|
|
||||||
def test_product_available_by_time(self):
|
def test_product_available_by_time(self):
|
||||||
""" The product is available if now is in the right timeframe. """
|
"""The product is available if now is in the right timeframe."""
|
||||||
product = ProductFactory()
|
product = ProductFactory()
|
||||||
# The factory defines the timeframe as now and 31 days forward.
|
# The factory defines the timeframe as now and 31 days forward.
|
||||||
self.assertTrue(product.is_time_available)
|
self.assertTrue(product.is_time_available)
|
||||||
self.assertTrue(product.is_available())
|
self.assertTrue(product.is_available())
|
||||||
|
|
||||||
def test_product_not_available_by_time(self):
|
def test_product_not_available_by_time(self):
|
||||||
""" The product is not available if now is outside the timeframe. """
|
"""The product is not available if now is outside the timeframe."""
|
||||||
available_in = DateTimeTZRange(
|
available_in = DateTimeTZRange(
|
||||||
lower=timezone.now() - timezone.timedelta(5),
|
lower=timezone.now() - timezone.timedelta(5),
|
||||||
upper=timezone.now() - timezone.timedelta(1),
|
upper=timezone.now() - timezone.timedelta(1),
|
||||||
|
@ -57,7 +58,7 @@ class ProductAvailabilityTest(TestCase):
|
||||||
self.assertFalse(product.is_available())
|
self.assertFalse(product.is_available())
|
||||||
|
|
||||||
def test_product_is_not_available_yet(self):
|
def test_product_is_not_available_yet(self):
|
||||||
""" The product is not available because we are before lower bound. """
|
"""The product is not available because we are before lower bound."""
|
||||||
available_in = DateTimeTZRange(lower=timezone.now() + timezone.timedelta(5))
|
available_in = DateTimeTZRange(lower=timezone.now() + timezone.timedelta(5))
|
||||||
product = ProductFactory(available_in=available_in)
|
product = ProductFactory(available_in=available_in)
|
||||||
# Make sure there is no upper - just in case.
|
# Make sure there is no upper - just in case.
|
||||||
|
@ -67,7 +68,7 @@ class ProductAvailabilityTest(TestCase):
|
||||||
self.assertFalse(product.is_available())
|
self.assertFalse(product.is_available())
|
||||||
|
|
||||||
def test_product_is_available_from_now_on(self):
|
def test_product_is_available_from_now_on(self):
|
||||||
""" The product is available because we are after lower bound. """
|
"""The product is available because we are after lower bound."""
|
||||||
available_in = DateTimeTZRange(lower=timezone.now() - timezone.timedelta(1))
|
available_in = DateTimeTZRange(lower=timezone.now() - timezone.timedelta(1))
|
||||||
product = ProductFactory(available_in=available_in)
|
product = ProductFactory(available_in=available_in)
|
||||||
# Make sure there is no upper - just in case.
|
# Make sure there is no upper - just in case.
|
||||||
|
|
|
@ -19,6 +19,7 @@ from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.generic import DetailView, FormView, ListView, View
|
from django.views.generic import DetailView, FormView, ListView, View
|
||||||
from django.views.generic.base import RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from shop.models import (
|
from shop.models import (
|
||||||
CreditNote,
|
CreditNote,
|
||||||
EpayCallback,
|
EpayCallback,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from teams.models import Team
|
from teams.models import Team
|
||||||
from utils.email import add_outgoing_email
|
from utils.email import add_outgoing_email
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.models import Camp
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from camps.models import Camp
|
||||||
from sponsors.email import add_sponsorticket_email
|
from sponsors.email import add_sponsorticket_email
|
||||||
from sponsors.models import Sponsor
|
from sponsors.models import Sponsor
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
from camps.models import Camp
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from camps.models import Camp
|
||||||
from sponsors.models import Sponsor
|
from sponsors.models import Sponsor
|
||||||
from tickets.models import SponsorTicket, TicketType
|
from tickets.models import SponsorTicket, TicketType
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
# Generated by Django 1.11 on 2017-07-11 21:35
|
# Generated by Django 1.11 on 2017-07-11 21:35
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import sponsors.models
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import sponsors.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from utils.models import CampRelatedModel
|
from utils.models import CampRelatedModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from .models import Sponsor
|
from .models import Sponsor
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from camps.utils import CampPropertyListFilter
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from camps.utils import CampPropertyListFilter
|
||||||
|
|
||||||
from .email import add_added_membership_email, add_removed_membership_email
|
from .email import add_added_membership_email, add_removed_membership_email
|
||||||
from .models import Team, TeamMember, TeamShift, TeamTask
|
from .models import Team, TeamMember, TeamShift, TeamTask
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ from django.contrib.postgres.fields import DateTimeRangeField
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
|
||||||
from utils.slugs import unique_slugify
|
from utils.slugs import unique_slugify
|
||||||
|
|
||||||
|
@ -317,7 +318,7 @@ class TeamMember(CampRelatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def camp(self):
|
def camp(self):
|
||||||
""" All CampRelatedModels must have a camp FK or a camp property """
|
"""All CampRelatedModels must have a camp FK or a camp property"""
|
||||||
return self.team.camp
|
return self.team.camp
|
||||||
|
|
||||||
camp_filter = "team__camp"
|
camp_filter = "team__camp"
|
||||||
|
@ -362,7 +363,7 @@ class TeamTask(CampRelatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def camp(self):
|
def camp(self):
|
||||||
""" All CampRelatedModels must have a camp FK or a camp property """
|
"""All CampRelatedModels must have a camp FK or a camp property"""
|
||||||
return self.team.camp
|
return self.team.camp
|
||||||
|
|
||||||
camp_filter = "team__camp"
|
camp_filter = "team__camp"
|
||||||
|
@ -406,7 +407,7 @@ class TeamShift(CampRelatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def camp(self):
|
def camp(self):
|
||||||
""" All CampRelatedModels must have a camp FK or a camp property """
|
"""All CampRelatedModels must have a camp FK or a camp property"""
|
||||||
return self.team.camp
|
return self.team.camp
|
||||||
|
|
||||||
camp_filter = "team__camp"
|
camp_filter = "team__camp"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,6 +12,7 @@ def teammember_saved(sender, instance, created, **kwargs):
|
||||||
# call the mail sending function
|
# call the mail sending function
|
||||||
# late import to please django 3.2 or "django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet."
|
# late import to please django 3.2 or "django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet."
|
||||||
from .email import add_new_membership_email
|
from .email import add_new_membership_email
|
||||||
|
|
||||||
if not add_new_membership_email(instance):
|
if not add_new_membership_email(instance):
|
||||||
logger.error("Error adding email to outgoing queue")
|
logger.error("Error adding email to outgoing queue")
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django import template
|
from django import template
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from teams.models import TeamMember
|
from teams.models import TeamMember
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
from teams.views.base import (
|
from teams.views.base import (
|
||||||
FixIrcAclView,
|
FixIrcAclView,
|
||||||
TeamGeneralView,
|
TeamGeneralView,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
@ -9,6 +8,8 @@ from django.urls import reverse_lazy
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.edit import UpdateView
|
from django.views.generic.edit import UpdateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from ..models import Team, TeamMember
|
from ..models import Team, TeamMember
|
||||||
from .mixins import EnsureTeamResponsibleMixin
|
from .mixins import EnsureTeamResponsibleMixin
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||||
from django.views.generic import DetailView
|
from django.views.generic import DetailView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from ..models import Team, TeamMember
|
from ..models import Team, TeamMember
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
|
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
|
||||||
from info.models import InfoCategory, InfoItem
|
|
||||||
from reversion.views import RevisionMixin
|
from reversion.views import RevisionMixin
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
from info.models import InfoCategory, InfoItem
|
||||||
|
|
||||||
from ..models import Team
|
from ..models import Team
|
||||||
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin
|
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.views.generic import DetailView, UpdateView
|
from django.views.generic import DetailView, UpdateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
from profiles.models import Profile
|
from profiles.models import Profile
|
||||||
|
|
||||||
from ..email import add_added_membership_email, add_removed_membership_email
|
from ..email import add_added_membership_email, add_removed_membership_email
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from teams.models import Team, TeamMember
|
from teams.models import Team, TeamMember
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
@ -17,6 +16,8 @@ from django.views.generic import (
|
||||||
)
|
)
|
||||||
from psycopg2.extras import DateTimeTZRange
|
from psycopg2.extras import DateTimeTZRange
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from ..models import Team, TeamMember, TeamShift
|
from ..models import Team, TeamMember, TeamShift
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from camps.mixins import CampViewMixin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpResponseNotAllowed, HttpResponseRedirect
|
from django.http import HttpResponseNotAllowed, HttpResponseRedirect
|
||||||
from django.views.generic import CreateView, DetailView, UpdateView
|
from django.views.generic import CreateView, DetailView, UpdateView
|
||||||
|
|
||||||
|
from camps.mixins import CampViewMixin
|
||||||
|
|
||||||
from ..models import TaskComment, Team, TeamMember, TeamTask
|
from ..models import TaskComment, Team, TeamMember, TeamTask
|
||||||
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin
|
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue