run pre-commit --all-files, it's been a while since last time

This commit is contained in:
Thomas Steen Rasmussen 2021-07-19 15:06:10 +02:00
parent a21bc1097c
commit 01687ea11a
125 changed files with 623 additions and 397 deletions

View File

@ -1,4 +1,5 @@
from django import forms
from program.models import Event, Speaker
@ -46,7 +47,7 @@ class AutoScheduleApplyForm(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()

View File

@ -1,7 +1,8 @@
from camps.mixins import CampViewMixin
from django.contrib.auth.mixins import UserPassesTestMixin
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from camps.mixins import CampViewMixin
from economy.models import Pos
from utils.mixins import RaisePermissionRequiredMixin

View File

@ -395,7 +395,9 @@ urlpatterns = [
include(
[
path(
"", SpeakerDetailView.as_view(), name="speaker_detail",
"",
SpeakerDetailView.as_view(),
name="speaker_detail",
),
path(
"update/",
@ -473,7 +475,11 @@ urlpatterns = [
"<slug:slug>/",
include(
[
path("", EventDetailView.as_view(), name="event_detail",),
path(
"",
EventDetailView.as_view(),
name="event_detail",
),
path(
"update/",
EventUpdateView.as_view(),
@ -500,7 +506,11 @@ urlpatterns = [
"autoscheduler/",
include(
[
path("", AutoScheduleManageView.as_view(), name="autoschedule_manage",),
path(
"",
AutoScheduleManageView.as_view(),
name="autoschedule_manage",
),
path(
"crashcourse/",
AutoScheduleCrashCourseView.as_view(),
@ -512,7 +522,9 @@ urlpatterns = [
name="autoschedule_validate",
),
path(
"diff/", AutoScheduleDiffView.as_view(), name="autoschedule_diff",
"diff/",
AutoScheduleDiffView.as_view(),
name="autoschedule_diff",
),
path(
"apply/",
@ -539,7 +551,11 @@ urlpatterns = [
name="approve_event_feedback",
),
# add recording url objects
path("add_recording", AddRecordingView.as_view(), name="add_eventrecording",),
path(
"add_recording",
AddRecordingView.as_view(),
name="add_eventrecording",
),
# economy
path(
"economy/",
@ -658,18 +674,34 @@ urlpatterns = [
"pos/",
include(
[
path("", PosListView.as_view(), name="pos_list",),
path("create/", PosCreateView.as_view(), name="pos_create",),
path(
"",
PosListView.as_view(),
name="pos_list",
),
path(
"create/",
PosCreateView.as_view(),
name="pos_create",
),
path(
"<slug:pos_slug>/",
include(
[
path("", PosDetailView.as_view(), name="pos_detail",),
path(
"update/", PosUpdateView.as_view(), name="pos_update",
"",
PosDetailView.as_view(),
name="pos_detail",
),
path(
"delete/", PosDeleteView.as_view(), name="pos_delete",
"update/",
PosUpdateView.as_view(),
name="pos_update",
),
path(
"delete/",
PosDeleteView.as_view(),
name="pos_delete",
),
path(
"reports/",
@ -731,14 +763,30 @@ urlpatterns = [
"tokens/",
include(
[
path("", TokenListView.as_view(), name="token_list",),
path("create/", TokenCreateView.as_view(), name="token_create",),
path("stats/", TokenStatsView.as_view(), name="token_stats",),
path(
"",
TokenListView.as_view(),
name="token_list",
),
path(
"create/",
TokenCreateView.as_view(),
name="token_create",
),
path(
"stats/",
TokenStatsView.as_view(),
name="token_stats",
),
path(
"<int:pk>/",
include(
[
path("", TokenDetailView.as_view(), name="token_detail",),
path(
"",
TokenDetailView.as_view(),
name="token_detail",
),
path(
"update/",
TokenUpdateView.as_view(),

View File

@ -1,10 +1,10 @@
"""Backoffice views.py was split into multiple files in July 2021 /tyk."""
from .backoffice import * # noqa
from .content import * # noqa
from .economy import * # noqa
from .facilities import * # noqa
from .game import * # noqa
from .infodesk import * # noqa
from .orga import * # noqa
from .pos import * # noqa
from .program import * # noqa
from .backoffice import * # noqa
from .content import * # noqa
from .economy import * # noqa
from .facilities import * # noqa
from .game import * # noqa
from .infodesk import * # noqa
from .orga import * # noqa
from .pos import * # noqa
from .program import * # noqa

View File

@ -1,22 +1,20 @@
import logging
import requests
from camps.mixins import CampViewMixin
from django.conf import settings
from django.http import Http404, HttpResponse
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 utils.models import OutgoingEmail
from ..mixins import (
RaisePermissionRequiredMixin,
)
from ..mixins import RaisePermissionRequiredMixin
logger = logging.getLogger("bornhack.%s" % __name__)
class BackofficeIndexView(CampViewMixin, RaisePermissionRequiredMixin, TemplateView):
"""
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
@ -51,7 +56,7 @@ class BackofficeProxyView(CampViewMixin, RaisePermissionRequiredMixin, TemplateV
template_name = "backoffice_proxy.html"
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
if "proxy_slug" not in kwargs:
return super().dispatch(request, *args, **kwargs)

View File

@ -1,24 +1,16 @@
import logging
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.forms import modelformset_factory
from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic.edit import FormView
from program.models import (
Event,
EventFeedback,
Url,
UrlType,
)
from ..forms import (
AddRecordingForm,
)
from ..mixins import (
ContentTeamPermissionMixin,
)
from camps.mixins import CampViewMixin
from program.models import Event, EventFeedback, Url, UrlType
from ..forms import AddRecordingForm
from ..mixins import ContentTeamPermissionMixin
logger = logging.getLogger("bornhack.%s" % __name__)
@ -84,9 +76,7 @@ class AddRecordingView(CampViewMixin, ContentTeamPermissionMixin, FormView):
super().setup(*args, **kwargs)
self.queryset = Event.objects.filter(
track__camp=self.camp, video_recording=True
).exclude(
urls__url_type__name="Recording"
)
).exclude(urls__url_type__name="Recording")
self.form_class = modelformset_factory(
Event,
@ -113,19 +103,17 @@ class AddRecordingView(CampViewMixin, ContentTeamPermissionMixin, FormView):
form.save()
for event_data in form.cleaned_data:
if event_data['recording_url']:
url = event_data['recording_url']
if not event_data['id'].urls.filter(url=url).exists():
if event_data["recording_url"]:
url = event_data["recording_url"]
if not event_data["id"].urls.filter(url=url).exists():
recording_url = Url()
recording_url.event = event_data['id']
recording_url.event = event_data["id"]
recording_url.url = url
recording_url.url_type = UrlType.objects.get(name="Recording")
recording_url.save()
if form.changed_objects:
messages.success(
self.request, f"Updated {len(form.changed_objects)} Event"
)
messages.success(self.request, f"Updated {len(form.changed_objects)} Event")
return redirect(self.get_success_url())
def get_success_url(self, *args, **kwargs):

View File

@ -1,7 +1,6 @@
import logging
import os
from camps.mixins import CampViewMixin
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import User
@ -12,18 +11,12 @@ from django.urls import reverse
from django.utils import timezone
from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from economy.models import (
Chain,
Credebtor,
Expense,
Reimbursement,
Revenue,
)
from camps.mixins import CampViewMixin
from economy.models import Chain, Credebtor, Expense, Reimbursement, Revenue
from teams.models import Team
from ..mixins import (
EconomyTeamPermissionMixin,
)
from ..mixins import EconomyTeamPermissionMixin
logger = logging.getLogger("bornhack.%s" % __name__)
@ -245,7 +238,7 @@ class ReimbursementCreateView(CampViewMixin, EconomyTeamPermissionMixin, CreateV
fields = ["notes", "paid"]
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"])
# get response now so we have self.camp available below
@ -448,5 +441,3 @@ class RevenueDetailView(CampViewMixin, EconomyTeamPermissionMixin, UpdateView):
return redirect(
reverse("backoffice:revenue_list", kwargs={"camp_slug": self.camp.slug})
)

View File

@ -1,6 +1,5 @@
import logging
from camps.mixins import CampViewMixin
from django.conf import settings
from django.contrib import messages
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.views.generic import DetailView, ListView
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 (
Facility,
FacilityFeedback,
FacilityOpeningHours,
FacilityType,
)
from leaflet.forms.widgets import LeafletWidget
from teams.models import Team
from ..mixins import (
OrgaTeamPermissionMixin,
RaisePermissionRequiredMixin,
)
from ..mixins import OrgaTeamPermissionMixin, RaisePermissionRequiredMixin
logger = logging.getLogger("bornhack.%s" % __name__)
@ -320,5 +318,3 @@ class FacilityOpeningHoursDeleteView(
"backoffice:facility_detail",
kwargs={"camp_slug": self.camp.slug, "facility_uuid": self.facility.pk},
)

View File

@ -1,6 +1,5 @@
import logging
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.contrib.auth.models import User
from django.db.models import Count, Q
@ -8,11 +7,11 @@ from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from camps.mixins import CampViewMixin
from tokens.models import Token, TokenFind
from ..mixins import (
RaisePermissionRequiredMixin,
)
from ..mixins import RaisePermissionRequiredMixin
logger = logging.getLogger("bornhack.%s" % __name__)

View File

@ -1,16 +1,15 @@
import logging
from itertools import chain
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView, TemplateView
from camps.mixins import CampViewMixin
from shop.models import Order, OrderProductRelation
from tickets.models import DiscountTicket, ShopTicket, SponsorTicket, TicketType
from ..mixins import (
InfoTeamPermissionMixin,
)
from ..mixins import InfoTeamPermissionMixin
logger = logging.getLogger("bornhack.%s" % __name__)
@ -140,7 +139,9 @@ class ScanTicketsView(
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
template_name = "shop_ticket_overview.html"
context_object_name = "shop_tickets"

View File

@ -1,6 +1,5 @@
import logging
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.forms import modelformset_factory
from django.shortcuts import redirect
@ -8,13 +7,13 @@ from django.urls import reverse
from django.utils import timezone
from django.views.generic import ListView, TemplateView
from django.views.generic.edit import FormView
from camps.mixins import CampViewMixin
from profiles.models import Profile
from shop.models import OrderProductRelation
from utils.models import OutgoingEmail
from ..mixins import (
OrgaTeamPermissionMixin,
)
from ..mixins import OrgaTeamPermissionMixin
logger = logging.getLogger("bornhack.%s" % __name__)
@ -28,6 +27,7 @@ class ApproveNamesView(CampViewMixin, OrgaTeamPermissionMixin, ListView):
public_credit_name=""
)
################################
# MERCHANDISE VIEWS

View File

@ -1,23 +1,17 @@
import logging
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from economy.models import (
Pos,
PosReport,
)
from camps.mixins import CampViewMixin
from economy.models import Pos, PosReport
from teams.models import Team
from ..mixins import (
OrgaTeamPermissionMixin,
PosViewMixin,
RaisePermissionRequiredMixin,
)
from ..mixins import OrgaTeamPermissionMixin, PosViewMixin, RaisePermissionRequiredMixin
logger = logging.getLogger("bornhack.%s" % __name__)

View File

@ -1,6 +1,5 @@
import logging
from camps.mixins import CampViewMixin
from django import forms
from django.conf import settings
from django.contrib import messages
@ -10,6 +9,8 @@ from django.urls import reverse
from django.utils.safestring import mark_safe
from django.views.generic import DetailView, ListView, TemplateView
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from camps.mixins import CampViewMixin
from program.autoscheduler import AutoScheduler
from program.mixins import AvailabilityMatrixViewMixin
from program.models import (
@ -30,9 +31,7 @@ from ..forms import (
EventScheduleForm,
SpeakerForm,
)
from ..mixins import (
ContentTeamPermissionMixin,
)
from ..mixins import ContentTeamPermissionMixin
logger = logging.getLogger("bornhack.%s" % __name__)
@ -42,7 +41,7 @@ logger = logging.getLogger("bornhack.%s" % __name__)
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
template_name = "pending_proposals.html"
@ -88,7 +87,7 @@ class ProposalApproveBaseView(CampViewMixin, ContentTeamPermissionMixin, UpdateV
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
template_name = "speaker_proposal_list.html"
@ -105,7 +104,7 @@ class SpeakerProposalDetailView(
ContentTeamPermissionMixin,
DetailView,
):
""" This view permits Content Team members to see SpeakerProposal details """
"""This view permits Content Team members to see SpeakerProposal details"""
model = SpeakerProposal
template_name = "speaker_proposal_detail_backoffice.html"
@ -118,7 +117,7 @@ class SpeakerProposalDetailView(
class SpeakerProposalApproveRejectView(ProposalApproveBaseView):
""" This view allows ContentTeam members to approve/reject SpeakerProposals """
"""This view allows ContentTeam members to approve/reject SpeakerProposals"""
model = SpeakerProposal
template_name = "speaker_proposal_approve_reject.html"
@ -126,7 +125,7 @@ class SpeakerProposalApproveRejectView(ProposalApproveBaseView):
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
template_name = "event_proposal_list.html"
@ -147,7 +146,7 @@ class EventProposalListView(CampViewMixin, ContentTeamPermissionMixin, ListView)
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
template_name = "event_proposal_detail_backoffice.html"
@ -155,7 +154,7 @@ class EventProposalDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailV
class EventProposalApproveRejectView(ProposalApproveBaseView):
""" This view allows ContentTeam members to approve/reject EventProposals """
"""This view allows ContentTeam members to approve/reject EventProposals"""
model = EventProposal
template_name = "event_proposal_approve_reject.html"
@ -167,7 +166,7 @@ class EventProposalApproveRejectView(ProposalApproveBaseView):
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
template_name = "speaker_list_backoffice.html"
@ -186,7 +185,7 @@ class SpeakerListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
class SpeakerDetailView(
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
template_name = "speaker_detail_backoffice.html"
@ -202,20 +201,20 @@ class SpeakerDetailView(
class SpeakerUpdateView(
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
template_name = "speaker_update.html"
form_class = SpeakerForm
def get_form_kwargs(self):
""" Set camp for the form """
"""Set camp for the form"""
kwargs = super().get_form_kwargs()
kwargs.update({"camp": self.camp})
return kwargs
def form_valid(self, form):
""" Save object and availability """
"""Save object and availability"""
speaker = form.save()
save_speaker_availability(form, obj=speaker)
messages.success(self.request, "Speaker has been updated")
@ -228,7 +227,7 @@ class SpeakerUpdateView(
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
template_name = "speaker_delete.html"
@ -252,7 +251,7 @@ class SpeakerDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteView):
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
template_name = "event_type_list.html"
@ -282,7 +281,7 @@ class EventTypeListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
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
template_name = "event_type_detail.html"
@ -306,7 +305,7 @@ class EventTypeDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView)
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
template_name = "event_location_list.html"
@ -319,7 +318,7 @@ class EventLocationListView(CampViewMixin, ContentTeamPermissionMixin, ListView)
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
template_name = "event_location_detail.html"
@ -334,7 +333,7 @@ class EventLocationDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailV
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
fields = ["name", "icon", "capacity", "conflicts"]
@ -362,7 +361,7 @@ class EventLocationCreateView(CampViewMixin, ContentTeamPermissionMixin, CreateV
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
fields = ["name", "icon", "capacity", "conflicts"]
@ -386,7 +385,7 @@ class EventLocationUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateV
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
template_name = "event_location_delete.html"
@ -414,7 +413,7 @@ class EventLocationDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteV
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
template_name = "event_list_backoffice.html"
@ -431,14 +430,14 @@ class EventListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
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
template_name = "event_detail_backoffice.html"
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
fields = [
@ -460,7 +459,7 @@ class EventUpdateView(CampViewMixin, ContentTeamPermissionMixin, UpdateView):
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
template_name = "event_delete.html"
@ -721,7 +720,7 @@ class EventSessionDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteVi
context_object_name = "session"
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():
messages.warning(
self.request,
@ -749,7 +748,7 @@ class EventSessionDeleteView(CampViewMixin, ContentTeamPermissionMixin, DeleteVi
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
template_name = "event_slot_list.html"
@ -766,7 +765,7 @@ class EventSlotListView(CampViewMixin, ContentTeamPermissionMixin, ListView):
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
template_name = "event_slot_detail.html"
@ -774,7 +773,7 @@ class EventSlotDetailView(CampViewMixin, ContentTeamPermissionMixin, DetailView)
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
template_name = "event_slot_unschedule.html"
@ -802,7 +801,7 @@ class EventSlotUnscheduleView(CampViewMixin, ContentTeamPermissionMixin, UpdateV
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"
@ -810,7 +809,7 @@ class AutoScheduleManageView(CampViewMixin, ContentTeamPermissionMixin, Template
class AutoScheduleCrashCourseView(
CampViewMixin, ContentTeamPermissionMixin, TemplateView
):
""" A short crash course on the autoscheduler """
"""A short crash course on the autoscheduler"""
template_name = "autoschedule_crash_course.html"

View File

@ -1,4 +1,5 @@
from django.db import models
from utils.models import CampRelatedModel

View File

@ -1,6 +1,7 @@
from camps.mixins import CampViewMixin
from django.views.generic import ListView
from camps.mixins import CampViewMixin
from .models import ProductCategory

View File

@ -1,6 +1,7 @@
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from program.consumers import ScheduleConsumer
application = ProtocolTypeRouter(

View File

@ -1,7 +1,8 @@
from camps.models import Camp
from graphene import ObjectType, Schema, relay
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from camps.models import Camp
from program.schema import ProgramQuery

View File

@ -203,4 +203,4 @@ DATA_UPLOAD_MAX_NUMBER_FIELDS = 5000
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
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

View File

@ -1,12 +1,13 @@
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.urls import include
from django.contrib import admin
from django.contrib.auth.decorators import login_required
from django.urls import path
from django.views.generic import TemplateView
from bar.views import MenuView
from camps.views import CampDetailView, CampListView, CampRedirectView
from feedback.views import FeedbackCreate
from info.views import CampInfoView
from people.views import PeopleView

View File

@ -1,6 +1,7 @@
from camps.models import Camp
from django.shortcuts import get_object_or_404
from camps.models import Camp
class CampViewMixin:
"""

View File

@ -8,6 +8,7 @@ from django.db import models
from django.urls import reverse
from django.utils import timezone
from psycopg2.extras import DateTimeTZRange
from utils.models import CreatedUpdatedModel, UUIDModel
logger = logging.getLogger("bornhack.%s" % __name__)
@ -135,7 +136,7 @@ class Camp(CreatedUpdatedModel, UUIDModel):
return reverse("camp_detail", kwargs={"camp_slug": self.slug})
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 = []
# check for overlaps buildup vs. camp
if self.buildup.upper > self.camp.lower:
@ -258,7 +259,7 @@ class Camp(CreatedUpdatedModel, UUIDModel):
@property
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")
return EventType.objects.filter(
events__isnull=False, event__track__camp=self

View File

@ -1,7 +1,8 @@
from camps.models import Camp
from django.contrib import admin
from django.utils import timezone
from camps.models import Camp
def get_current_camp():
try:

View File

@ -1,6 +1,7 @@
import os
from django.conf import settings
from teams.models import Team
from utils.email import add_outgoing_email

View File

@ -53,7 +53,9 @@ class Migration(migrations.Migration):
),
),
],
options={"ordering": ["name"],},
options={
"ordering": ["name"],
},
),
migrations.CreateModel(
name="PosReport",
@ -279,6 +281,8 @@ class Migration(migrations.Migration):
),
),
],
options={"abstract": False,},
options={
"abstract": False,
},
),
]

View File

@ -11,6 +11,7 @@ class Migration(migrations.Migration):
operations = [
migrations.AlterModelOptions(
name="posreport", options={"ordering": ["date", "pos"]},
name="posreport",
options={"ordering": ["date", "pos"]},
),
]

View File

@ -4,6 +4,7 @@ from django.contrib import messages
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
from utils.slugs import unique_slugify

View File

@ -1,7 +1,6 @@
import os
import magic
from camps.mixins import CampViewMixin
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
@ -16,6 +15,8 @@ from django.views.generic import (
TemplateView,
UpdateView,
)
from camps.mixins import CampViewMixin
from teams.models import Team
from utils.email import add_outgoing_email
from utils.mixins import RaisePermissionRequiredMixin

View File

@ -1,4 +1,5 @@
from django.db import models
from teams.models import Team
from utils.models import CreatedUpdatedModel

View File

@ -42,7 +42,9 @@ class Migration(migrations.Migration):
models.TextField(help_text="Description of this facility"),
),
],
options={"abstract": False,},
options={
"abstract": False,
},
),
migrations.CreateModel(
name="FacilityQuickFeedback",
@ -124,7 +126,9 @@ class Migration(migrations.Migration):
),
),
],
options={"unique_together": {("slug", "responsible_team")},},
options={
"unique_together": {("slug", "responsible_team")},
},
),
migrations.CreateModel(
name="FacilityFeedback",
@ -202,7 +206,9 @@ class Migration(migrations.Migration):
),
),
],
options={"abstract": False,},
options={
"abstract": False,
},
),
migrations.AddField(
model_name="facility",

View File

@ -50,6 +50,8 @@ class Migration(migrations.Migration):
),
),
],
options={"abstract": False,},
options={
"abstract": False,
},
),
]

View File

@ -14,7 +14,8 @@ class Migration(migrations.Migration):
operations = [
migrations.AlterModelOptions(
name="facilityopeninghours", options={"ordering": ["when"]},
name="facilityopeninghours",
options={"ordering": ["when"]},
),
migrations.AlterField(
model_name="facility",

View File

@ -1,6 +1,7 @@
from camps.mixins import CampViewMixin
from django.shortcuts import get_object_or_404
from camps.mixins import CampViewMixin
from .models import Facility, FacilityType

View File

@ -9,6 +9,7 @@ from django.contrib.postgres.constraints import ExclusionConstraint
from django.contrib.postgres.fields import DateTimeRangeField, RangeOperators
from django.db import models
from django.shortcuts import reverse
from maps.utils import LeafletMarkerChoices
from utils.models import CampRelatedModel, UUIDModel
from utils.slugs import unique_slugify
@ -111,7 +112,8 @@ class Facility(CampRelatedModel, UUIDModel):
)
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")
@ -250,7 +252,8 @@ class FacilityOpeningHours(CampRelatedModel):
)
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(

View File

@ -1,4 +1,3 @@
from camps.mixins import CampViewMixin
from django import forms
from django.contrib import messages
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.edit import CreateView
from camps.mixins import CampViewMixin
from .mixins import FacilityTypeViewMixin, FacilityViewMixin
from .models import Facility, FacilityFeedback, FacilityType

View File

@ -1,4 +1,5 @@
from django.db import models
from utils.models import CampRelatedModel, UUIDModel

View File

@ -1,9 +1,10 @@
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views.generic import CreateView
from camps.mixins import CampViewMixin
from tokens.models import Token
from .models import Feedback

View File

@ -1,6 +1,7 @@
import reversion
from django.core.exceptions import ValidationError
from django.db import models
from utils.models import CampRelatedModel

View File

@ -1,6 +1,7 @@
from camps.mixins import CampViewMixin
from django.views.generic import ListView
from camps.mixins import CampViewMixin
from .models import InfoCategory

View File

@ -7,6 +7,7 @@ import irc3
from asgiref.sync import sync_to_async
from django.conf import settings
from django.utils import timezone
from ircbot.models import OutgoingIrcMessage
from teams.models import Team, TeamMember
from teams.utils import get_team_from_irc_channel
@ -128,8 +129,8 @@ class Plugin(object):
@irc3.extend
def get_outgoing_messages(self):
"""
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.
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.
"""
for msg in OutgoingIrcMessage.objects.filter(processed=False).order_by(
"created"

View File

@ -9,7 +9,7 @@ logger = logging.getLogger("bornhack.%s" % __name__)
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"):
logger.error(

View File

@ -1,6 +1,7 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from utils.models import CreatedUpdatedModel

View File

@ -1,6 +1,7 @@
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
from utils.models import CreatedUpdatedModel
from utils.slugs import unique_slugify

View File

@ -1,6 +1,7 @@
from camps.models import Camp
from django.views.generic import ListView
from camps.models import Camp
class PeopleView(ListView):
template_name = "people.html"

View File

@ -82,6 +82,8 @@ class Migration(migrations.Migration):
),
),
],
options={"unique_together": {("camp", "number")},},
options={
"unique_together": {("camp", "number")},
},
),
]

View File

@ -3,6 +3,7 @@ import logging
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db import models
from utils.models import CampRelatedModel
from .dectutils import DectUtils
@ -20,7 +21,9 @@ class DectRegistration(CampRelatedModel):
unique_together = [("camp", "number")]
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(
@ -47,11 +50,14 @@ class DectRegistration(CampRelatedModel):
)
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(
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):

View File

@ -3,7 +3,6 @@ import logging
import secrets
import string
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import ValidationError
@ -13,6 +12,8 @@ from django.urls import reverse
from django.utils import timezone
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
from oauth2_provider.views.generic import ProtectedResourceView
from camps.mixins import CampViewMixin
from utils.mixins import RaisePermissionRequiredMixin, UserIsObjectOwnerMixin
from .mixins import DectRegistrationViewMixin

View File

@ -1,6 +1,7 @@
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _
from utils.models import CreatedUpdatedModel, UUIDModel

View File

@ -6,7 +6,7 @@ class ProgramConfig(AppConfig):
name = "program"
def ready(self):
from .models import Speaker, EventSession
from .models import EventSession, Speaker
from .signal_handlers import (
check_speaker_event_camp_consistency,
event_session_post_save,

View File

@ -25,7 +25,7 @@ class AutoScheduler:
"""
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
# Get all EventTypes which support autoscheduling
@ -62,17 +62,17 @@ class AutoScheduler:
self.autoevents, self.autoeventindex = self.get_autoevents(self.events)
def get_event_types(self):
""" Return all EventTypes which support autoscheduling """
"""Return all EventTypes which support autoscheduling"""
return EventType.objects.filter(support_autoscheduling=True)
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(
event_type__in=event_types,
).prefetch_related("event_type", "event_location")
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 self.camp.events.filter(event_type__in=event_types).exclude(
# exclude Events that have been sceduled already...
@ -82,7 +82,7 @@ class AutoScheduler:
)
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 = []
# loop over the sessions
for session in event_sessions:
@ -92,7 +92,7 @@ class AutoScheduler:
return autoslots
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 = []
autoeventindex = {}
eventindex = {}
@ -197,10 +197,10 @@ class AutoScheduler:
return autoevents, autoeventindex
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
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
autoschedule = []
@ -244,8 +244,8 @@ class AutoScheduler:
return autoschedule
def calculate_autoschedule(self, original_schedule=None):
""" Calculate autoschedule based on self.autoevents and self.autoslots,
optionally using original_schedule to minimise changes """
"""Calculate autoschedule based on self.autoevents and self.autoslots,
optionally using original_schedule to minimise changes"""
kwargs = {}
kwargs["events"] = self.autoevents
kwargs["slots"] = self.autoslots
@ -264,8 +264,8 @@ class AutoScheduler:
return autoschedule
def calculate_similar_autoschedule(self, original_schedule=None):
""" Convenience method for creating similar schedules. If original_schedule
is omitted the new schedule is based on the current schedule instead """
"""Convenience method for creating similar schedules. If original_schedule
is omitted the new schedule is based on the current schedule instead"""
if not original_schedule:
# we do not have an original_schedule, use current EventInstances
@ -277,7 +277,7 @@ class AutoScheduler:
return autoschedule, diff
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
# 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
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 = []
for item in slot_diff:
@ -347,7 +350,8 @@ class AutoScheduler:
# then get a list of differences per event
event_diff = scheduler.event_schedule_difference(
original_schedule, new_schedule,
original_schedule,
new_schedule,
)
event_output = []
# loop over the differences and build the dict
@ -357,7 +361,11 @@ class AutoScheduler:
except self.camp.events.DoesNotExist:
event = item.event.name
event_output.append(
{"event": event, "old": {}, "new": {},}
{
"event": event,
"old": {},
"new": {},
}
)
# do we have an old slot for this event?
if item.old_slot:
@ -376,7 +384,7 @@ class AutoScheduler:
return {"event_diffs": event_output, "slot_diffs": slot_output}
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(
autoschedule, slots=self.autoslots, events=self.autoevents
)

View File

@ -1,6 +1,7 @@
from camps.models import Camp
from channels.generic.websocket import JsonWebsocketConsumer
from camps.models import Camp
from .models import (
Event,
EventInstance,

View File

@ -1,6 +1,7 @@
import logging
from django.core.exceptions import ObjectDoesNotExist
from teams.models import Team
from utils.email import add_outgoing_email

View File

@ -33,7 +33,8 @@ class SpeakerProposalForm(forms.ModelForm):
# only show events from this camp
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:
@ -265,9 +266,9 @@ class EventProposalForm(forms.ModelForm):
]
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"]:
raise forms.ValidationError(f"Please specify a duration.")
raise forms.ValidationError("Please specify a duration.")
if (
self.event_type.event_duration_minutes
and self.cleaned_data["duration"] > self.event_type.event_duration_minutes

View File

@ -2,10 +2,11 @@ import logging
from datetime import timedelta
from time import sleep
from camps.utils import get_current_camp
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone
from camps.utils import get_current_camp
from ircbot.models import OutgoingIrcMessage
from program.models import EventInstance

View File

@ -2,9 +2,10 @@
# Generated by Django 1.10.5 on 2017-02-18 11:43
from __future__ import unicode_literals
import program.models
from django.db import migrations, models
import program.models
class Migration(migrations.Migration):

View File

@ -5,10 +5,11 @@ from __future__ import unicode_literals
import uuid
import django.db.models.deletion
import program.models
from django.conf import settings
from django.db import migrations, models
import program.models
class Migration(migrations.Migration):

View File

@ -5,10 +5,11 @@ from __future__ import unicode_literals
import uuid
import django.db.models.deletion
import program.models
from django.conf import settings
from django.db import migrations, models
import program.models
class Migration(migrations.Migration):

View File

@ -2,9 +2,10 @@
# Generated by Django 1.10.5 on 2017-03-14 19:12
from __future__ import unicode_literals
import program.models
from django.db import migrations, models
import program.models
class Migration(migrations.Migration):

View File

@ -2,9 +2,10 @@
# Generated by Django 1.10.5 on 2017-03-15 23:04
from __future__ import unicode_literals
import program.models
from django.db import migrations, models
import program.models
class Migration(migrations.Migration):

View File

@ -29,6 +29,7 @@ class Migration(migrations.Migration):
),
),
migrations.AlterUniqueTogether(
name="eventfeedback", unique_together={("user", "event")},
name="eventfeedback",
unique_together={("user", "event")},
),
]

View File

@ -11,6 +11,8 @@ class Migration(migrations.Migration):
operations = [
migrations.RenameField(
model_name="eventfeedback", old_name="feedback", new_name="comment",
model_name="eventfeedback",
old_name="feedback",
new_name="comment",
),
]

View File

@ -51,7 +51,9 @@ class Migration(migrations.Migration):
),
),
],
options={"ordering": ["when", "event_type", "event_location"],},
options={
"ordering": ["when", "event_type", "event_location"],
},
),
migrations.CreateModel(
name="EventSlot",
@ -81,7 +83,9 @@ class Migration(migrations.Migration):
),
),
],
options={"ordering": ["when"],},
options={
"ordering": ["when"],
},
),
migrations.CreateModel(
name="SpeakerAvailability",
@ -128,7 +132,9 @@ class Migration(migrations.Migration):
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
],
options={"abstract": False,},
options={
"abstract": False,
},
),
migrations.CreateModel(
name="SpeakerProposalAvailability",
@ -175,7 +181,9 @@ class Migration(migrations.Migration):
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
],
options={"abstract": False,},
options={
"abstract": False,
},
),
migrations.AddField(
model_name="event",

View File

@ -32,13 +32,19 @@ class Migration(migrations.Migration):
new_name="speaker_proposal",
),
migrations.RenameField(
model_name="url", old_name="eventproposal", new_name="event_proposal",
model_name="url",
old_name="eventproposal",
new_name="event_proposal",
),
migrations.RenameField(
model_name="url", old_name="speakerproposal", new_name="speaker_proposal",
model_name="url",
old_name="speakerproposal",
new_name="speaker_proposal",
),
migrations.RenameField(
model_name="url", old_name="urltype", new_name="url_type",
model_name="url",
old_name="urltype",
new_name="url_type",
),
migrations.AlterField(
model_name="event",

View File

@ -2,9 +2,10 @@
import django.contrib.postgres.constraints
import django.db.models.deletion
import utils.database
from django.db import migrations, models
import utils.database
class Migration(migrations.Migration):
@ -13,13 +14,21 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RemoveField(model_name="speakereventconflict", name="events",),
migrations.RemoveField(model_name="speakereventconflict", name="speaker",),
migrations.RemoveField(
model_name="speakerproposaleventconflict", name="events",
model_name="speakereventconflict",
name="events",
),
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(
model_name="eventtype",
@ -110,6 +119,10 @@ class Migration(migrations.Migration):
name="prevent_speaker_proposal_availability_adjacent_mergeable",
),
),
migrations.DeleteModel(name="SpeakerEventConflict",),
migrations.DeleteModel(name="SpeakerProposalEventConflict",),
migrations.DeleteModel(
name="SpeakerEventConflict",
),
migrations.DeleteModel(
name="SpeakerProposalEventConflict",
),
]

View File

@ -1,9 +1,10 @@
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.views.generic.detail import SingleObjectMixin
from camps.mixins import CampViewMixin
from program.utils import (
add_existing_availability_to_matrix,
get_speaker_availability_form_matrix,
@ -141,7 +142,9 @@ class EventFeedbackViewMixin(EventViewMixin):
def setup(self, *args, **kwargs):
super().setup(*args, **kwargs)
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):
@ -157,7 +160,7 @@ class AvailabilityMatrixViewMixin(CampViewMixin):
"""
def setup(self, *args, **kwargs):
""" Get the availability matrix"""
"""Get the availability matrix"""
super().setup(*args, **kwargs)
# do we have an Event or an EventProposal?
if hasattr(self.get_object(), "events"):
@ -177,13 +180,13 @@ class AvailabilityMatrixViewMixin(CampViewMixin):
add_existing_availability_to_matrix(self.matrix, self.get_object())
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["matrix"] = self.matrix
return 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)
# add initial checkbox states
@ -202,7 +205,7 @@ class AvailabilityMatrixViewMixin(CampViewMixin):
return initial
def get_context_data(self, **kwargs):
""" Add the matrix to context """
"""Add the matrix to context"""
context = super().get_context_data(**kwargs)
context["matrix"] = self.matrix
return context

View File

@ -18,6 +18,7 @@ from django.utils import timezone
from django.utils.safestring import mark_safe
from psycopg2.extras import DateTimeTZRange
from taggit.managers import TaggableManager
from utils.database import CastToInteger
from utils.models import (
CampRelatedModel,
@ -119,7 +120,7 @@ class Url(CampRelatedModel):
return self.url
def clean_fk(self):
""" Make sure we have exactly one FK """
"""Make sure we have exactly one FK"""
fks = 0
if self.speaker_proposal:
fks += 1
@ -192,10 +193,10 @@ class Availability(CampRelatedModel, UUIDModel):
class SpeakerProposalAvailability(Availability):
""" Availability info for SpeakerProposal objects """
"""Availability info for SpeakerProposal objects"""
class Meta:
""" Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability """
"""Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability"""
constraints = [
# we do not want overlapping ranges
@ -247,10 +248,10 @@ class SpeakerProposalAvailability(Availability):
class SpeakerAvailability(Availability):
""" Availability info for Speaker objects """
"""Availability info for Speaker objects"""
class Meta:
""" Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability """
"""Add ExclusionConstraints preventing overlaps and adjacent ranges with same availability"""
constraints = [
# we do not want overlapping ranges
@ -290,7 +291,9 @@ class SpeakerAvailability(Availability):
def clean(self):
# this should be an ExclusionConstraint but the boolean condition isn't conditioning :/
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():
raise ValidationError(
"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):
"""
An abstract model containing the stuff that is shared
between the SpeakerProposal and EventProposal models.
An abstract model containing the stuff that is shared
between the SpeakerProposal and EventProposal models.
"""
class Meta:
@ -359,7 +362,7 @@ class UserSubmittedModel(CampRelatedModel):
class SpeakerProposal(UserSubmittedModel):
""" A speaker proposal """
"""A speaker proposal"""
camp = models.ForeignKey(
"camps.Camp",
@ -410,7 +413,7 @@ class SpeakerProposal(UserSubmittedModel):
)
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")
# create a Speaker if we don't have one
if not hasattr(self, "speaker"):
@ -470,14 +473,14 @@ class SpeakerProposal(UserSubmittedModel):
@property
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(
id__in=self.event_proposals.all().values_list("event_type", flat=True)
)
@property
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:
# we have no events, or events of different eventtypes, use generic title
return "Person"
@ -486,7 +489,7 @@ class SpeakerProposal(UserSubmittedModel):
class EventProposal(UserSubmittedModel):
""" An event proposal """
"""An event proposal"""
track = models.ForeignKey(
"program.EventTrack",
@ -538,7 +541,10 @@ class EventProposal(UserSubmittedModel):
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
def camp(self):
@ -618,7 +624,7 @@ class EventProposal(UserSubmittedModel):
@property
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():
return False
else:
@ -629,7 +635,7 @@ class EventProposal(UserSubmittedModel):
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")
@ -660,7 +666,7 @@ class EventTrack(CampRelatedModel):
class EventLocation(CampRelatedModel):
""" The places where stuff happens """
"""The places where stuff happens"""
name = models.CharField(max_length=100)
@ -693,7 +699,7 @@ class EventLocation(CampRelatedModel):
unique_together = (("camp", "slug"), ("camp", "name"))
def save(self, **kwargs):
""" Create a slug """
"""Create a slug"""
if not self.slug:
self.slug = unique_slugify(
self.name,
@ -715,11 +721,11 @@ class EventLocation(CampRelatedModel):
return self.camp.event_slots.filter(event_session__event_location=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)
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 (
self.event_slots.filter(event__isnull=False, when__overlap=when)
.exclude(pk__in=ignore_event_slot_ids)
@ -732,7 +738,7 @@ class EventLocation(CampRelatedModel):
class EventType(CreatedUpdatedModel):
""" Every event needs to have a type. """
"""Every event needs to have a type."""
name = models.CharField(
max_length=100, unique=True, help_text="The name of this event type"
@ -785,7 +791,8 @@ class EventType(CreatedUpdatedModel):
)
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(
@ -812,7 +819,7 @@ class EventType(CreatedUpdatedModel):
@property
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)
def icon_html(self):
@ -891,7 +898,8 @@ class EventSession(CampRelatedModel):
)
description = models.TextField(
blank=True, help_text="Description of this session (optional).",
blank=True,
help_text="Description of this session (optional).",
)
def __str__(self):
@ -904,12 +912,12 @@ class EventSession(CampRelatedModel):
@property
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
@property
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(
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)
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(
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),
)
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 = []
period = self.when
duration = timedelta(minutes=self.event_duration_minutes)
@ -985,7 +994,7 @@ class EventSession(CampRelatedModel):
return slots
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
needed_slot_times = set(self.get_slot_times(bounds="[)"))
@ -1036,7 +1045,9 @@ class EventSlot(CampRelatedModel):
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(
"program.Event",
@ -1064,7 +1075,7 @@ class EventSlot(CampRelatedModel):
return f"{self.when} ({self.event_session.event_location.name}, {self.event_session.event_type})"
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(
minutes=self.event_session.event_duration_minutes
):
@ -1087,7 +1098,7 @@ class EventSlot(CampRelatedModel):
self.clean_location()
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(
venue=self.event_session.event_location.id,
starts_at=self.when.lower,
@ -1105,7 +1116,7 @@ class EventSlot(CampRelatedModel):
return self.event_session.event_location
def clean_speakers(self):
""" Check if all speakers are available """
"""Check if all speakers are available"""
if self.event:
for speaker in self.event.speakers.all():
if not speaker.is_available(
@ -1116,7 +1127,7 @@ class EventSlot(CampRelatedModel):
)
def clean_location(self):
""" Make sure the location is available """
"""Make sure the location is available"""
if self.event:
if not self.event_location.is_available(
when=self.when, ignore_event_slot_ids=[self.pk]
@ -1126,14 +1137,14 @@ class EventSlot(CampRelatedModel):
)
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.autoscheduled = None
self.save()
@property
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:
return (self.event_location.capacity - self.event.demand) * -1
else:
@ -1207,7 +1218,7 @@ class EventSlot(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(
default=uuid.uuid4,
@ -1278,7 +1289,7 @@ class Event(CampRelatedModel):
return self.title
def save(self, **kwargs):
""" Create a slug and get duration """
"""Create a slug and get duration"""
if not self.slug:
self.slug = unique_slugify(
self.title,
@ -1336,7 +1347,7 @@ class Event(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(
default=uuid.uuid4,
@ -1362,7 +1373,8 @@ class EventInstance(CampRelatedModel):
)
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:
@ -1382,7 +1394,7 @@ class EventInstance(CampRelatedModel):
return "%s (%s)" % (self.event, self.when)
def clean_speakers(self):
""" Check if all speakers are available """
"""Check if all speakers are available"""
for speaker in self.event.speakers.all():
if not speaker.is_available(
when=self.event_slot.when, ignore_eventinstances=[self.pk]
@ -1392,7 +1404,7 @@ class EventInstance(CampRelatedModel):
)
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"]:
# we are saving for real
if clean_speakers:
@ -1408,11 +1420,11 @@ class EventInstance(CampRelatedModel):
@property
def schedule_date(self):
"""
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
an event is scheduled for 00:30 wednesday evening (technically thursday) then the date
after substracting 5 hours would be wednesdays date, not thursdays
(given settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS=5)
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
an event is scheduled for 00:30 wednesday evening (technically thursday) then the date
after substracting 5 hours would be wednesdays date, not thursdays
(given settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS=5)
"""
return (
self.when.lower - timedelta(hours=settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS)
@ -1420,7 +1432,7 @@ class EventInstance(CampRelatedModel):
@property
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
minutes = seconds / 60
return minutes / settings.SCHEDULE_TIMESLOT_LENGTH_MINUTES
@ -1470,7 +1482,7 @@ class EventInstance(CampRelatedModel):
@property
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
@property
@ -1482,7 +1494,7 @@ class EventInstance(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")
@ -1559,9 +1571,9 @@ class Speaker(CampRelatedModel):
return data
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
any availability at all """
any availability at all"""
if not self.availabilities.exists():
# we have no availability at all for this speaker, assume they are available
return True
@ -1585,12 +1597,12 @@ class Speaker(CampRelatedModel):
return True
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)
@property
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:
# we have different eventtypes, use generic title
return "Person"
@ -1638,7 +1650,8 @@ class EventFeedback(CampRelatedModel, UUIDModel):
)
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(
@ -1649,7 +1662,8 @@ class EventFeedback(CampRelatedModel, UUIDModel):
RATING_CHOICES = [(n, f"{n}") for n in range(0, 6)]
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?")
@ -1682,15 +1696,12 @@ class CustomUrlStorage(FileSystemStorage):
Can be removed when we clean up old migrations at some point
"""
pass
def get_speaker_picture_upload_path():
"""
Must exist because it is mentioned in old migrations.
Can be removed when we clean up old migrations at some point
"""
pass
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.
Can be removed when we clean up old migrations at some point
"""
pass
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.
Can be removed when we clean up old migrations at some point
"""
pass

View File

@ -50,7 +50,8 @@ class MultiForm(object):
# Some things, such as the WizardView expect these to exist.
self.data, self.files = data, files
kwargs.update(
data=data, files=files,
data=data,
files=files,
)
self.initials = kwargs.pop("initial", None)
@ -74,7 +75,8 @@ class MultiForm(object):
else:
prefix = "{0}__{1}".format(key, prefix)
fkwargs.update(
initial=self.initials.get(key), prefix=prefix,
initial=self.initials.get(key),
prefix=prefix,
)
return args, fkwargs

View File

@ -7,7 +7,7 @@ logger = logging.getLogger("bornhack.%s" % __name__)
def check_speaker_event_camp_consistency(sender, instance, **kwargs):
if kwargs["action"] == "pre_add":
from program.models import Speaker, Event
from program.models import Event, Speaker
if isinstance(instance, 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):
""" 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()

View File

@ -9,13 +9,13 @@ register = template.Library()
def render_datetime(datetime):
""" Renders a datetime in the users timezone """
"""Renders a datetime in the users timezone"""
t = Template("{{ datetime }}")
return t.render(Context({"datetime": datetime}))
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()})"
@ -32,7 +32,7 @@ def availabilitytable(matrix, form=None):
# start bulding the output
output = "<div class='form-group'>"
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)
# in the matrix and create a column for each

View File

@ -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
for et in event_types:
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
else:
@ -205,7 +209,9 @@ def save_speaker_availability(form, obj):
else:
# "available" changed or daychunk is not adjacent to formerchunk
AvailabilityModel.objects.create(
when=formerchunk, available=formeravailable, **kwargs,
when=formerchunk,
available=formeravailable,
**kwargs,
)
# and remember the current chunk for next iteration
formerchunk = daychunk

View File

@ -2,7 +2,6 @@ import logging
from collections import OrderedDict
import icalendar
from camps.mixins import CampViewMixin
from django import forms
from django.contrib import messages
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.edit import CreateView, DeleteView, UpdateView
from lxml import etree, objectify
from camps.mixins import CampViewMixin
from utils.middleware import RedirectException
from utils.mixins import UserIsObjectOwnerMixin
@ -82,7 +83,8 @@ class ICSView(CampViewMixin, View):
query_kwargs["event__video_recording"] = False
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")
cal = icalendar.Calendar()
@ -109,7 +111,7 @@ class ProposalListView(LoginRequiredMixin, CampViewMixin, ListView):
context_object_name = "speaker_proposal_list"
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()
@ -123,7 +125,7 @@ class ProposalListView(LoginRequiredMixin, CampViewMixin, ListView):
)
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["event_proposal_list"] = models.EventProposal.objects.filter(
track__camp=self.camp, user=self.request.user
@ -143,14 +145,14 @@ class SpeakerProposalCreateView(
EnsureCFPOpenMixin,
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
template_name = "speaker_proposal_form.html"
form_class = SpeakerProposalForm
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)
""" Get the event_proposal and availability matrix """
self.event_proposal = get_object_or_404(
@ -166,7 +168,7 @@ class SpeakerProposalCreateView(
return reverse("program:proposal_list", kwargs={"camp_slug": self.camp.slug})
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.update(
{
@ -178,7 +180,7 @@ class SpeakerProposalCreateView(
return kwargs
def get_initial(self, *args, **kwargs):
""" Populate the speaker_availability checkboxes """
"""Populate the speaker_availability checkboxes"""
initial = super().get_initial(*args, **kwargs)
# loop over dates in the matrix
for date in self.matrix.keys():
@ -196,7 +198,7 @@ class SpeakerProposalCreateView(
return context
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.user = self.request.user
speaker_proposal.camp = self.camp
@ -234,20 +236,20 @@ class SpeakerProposalUpdateView(
EnsureCFPOpenMixin,
UpdateView,
):
""" This view allows a user to update an existing SpeakerProposal. """
"""This view allows a user to update an existing SpeakerProposal."""
model = models.SpeakerProposal
template_name = "speaker_proposal_form.html"
form_class = SpeakerProposalForm
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 = qs.prefetch_related("availabilities")
return qs.get()
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()
# get all event types this SpeakerProposal is involved in
@ -269,7 +271,7 @@ class SpeakerProposalUpdateView(
return kwargs
def form_valid(self, form):
""" Change status and save availability """
"""Change status and save availability"""
speaker_proposal = form.save(commit=False)
# set proposal status to pending
@ -295,7 +297,7 @@ class SpeakerProposalUpdateView(
# message user and redirect
messages.info(
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(
reverse(
@ -335,7 +337,7 @@ class SpeakerProposalDeleteView(
return super().get(request, *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()
return super().delete(*args, **kwargs)
@ -377,18 +379,18 @@ class EventProposalTypeSelectView(
context_object_name = "event_type_list"
def setup(self, *args, **kwargs):
""" Get the speaker_proposal object """
"""Get the speaker_proposal object"""
super().setup(*args, **kwargs)
self.speaker = get_object_or_404(
models.SpeakerProposal, pk=kwargs["speaker_uuid"]
)
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)
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["speaker"] = self.speaker
return context
@ -410,18 +412,18 @@ class EventProposalSelectPersonView(
context_object_name = "speaker_proposal_list"
def dispatch(self, request, *args, **kwargs):
""" Get EventProposal from url kwargs """
"""Get EventProposal from url kwargs"""
self.event_proposal = get_object_or_404(
models.EventProposal, pk=kwargs["event_uuid"], user=request.user
)
return super().dispatch(request, *args, **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()
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["event_proposal"] = self.event_proposal
return context
@ -445,14 +447,14 @@ class EventProposalAddPersonView(
context_object_name = "event_proposal"
def dispatch(self, request, *args, **kwargs):
""" Get the speaker_proposal object """
"""Get the speaker_proposal object"""
self.speaker_proposal = get_object_or_404(
models.SpeakerProposal, pk=kwargs["speaker_uuid"], user=request.user
)
return super().dispatch(request, *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["speaker_proposal"] = self.speaker_proposal
return context
@ -488,7 +490,7 @@ class EventProposalRemovePersonView(
pk_url_kwarg = "event_uuid"
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
self.speaker_proposal = get_object_or_404(
models.SpeakerProposal, pk=kwargs["speaker_uuid"], user=request.user
@ -496,13 +498,13 @@ class EventProposalRemovePersonView(
return super().dispatch(request, *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["speaker_proposal"] = self.speaker_proposal
return context
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():
# this speaker is not associated with this event
raise Http404
@ -545,7 +547,7 @@ class EventProposalCreateView(
form_class = EventProposalForm
def setup(self, *args, **kwargs):
""" Get the speaker_proposal object """
"""Get the speaker_proposal object"""
super().setup(*args, **kwargs)
self.speaker_proposal = get_object_or_404(
models.SpeakerProposal, pk=self.kwargs["speaker_uuid"]
@ -555,7 +557,7 @@ class EventProposalCreateView(
)
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["speaker"] = self.speaker_proposal
context["event_type"] = self.event_type
@ -570,7 +572,7 @@ class EventProposalCreateView(
return kwargs
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.user = self.request.user
event_proposal.event_type = self.event_type
@ -630,7 +632,7 @@ class EventProposalUpdateView(
return 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["event_type"] = self.get_object().event_type
return context
@ -711,7 +713,7 @@ class CombinedProposalTypeSelectView(LoginRequiredMixin, CampViewMixin, ListView
template_name = "event_type_select.html"
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)
@ -727,14 +729,14 @@ class CombinedProposalPersonSelectView(LoginRequiredMixin, CampViewMixin, ListVi
context_object_name = "speaker_proposal_list"
def setup(self, *args, **kwargs):
""" Check that we have a valid EventType """
"""Check that we have a valid EventType"""
super().setup(*args, **kwargs)
self.event_type = get_object_or_404(
models.EventType, slug=self.kwargs["event_type_slug"]
)
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)
def get_context_data(self, **kwargs):
@ -746,7 +748,7 @@ class CombinedProposalPersonSelectView(LoginRequiredMixin, CampViewMixin, ListVi
return context
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():
return redirect(
reverse_lazy(
@ -1004,7 +1006,9 @@ class FrabXmlView(CampViewMixin, View):
qs = (
models.EventSlot.objects.filter(event__track__camp=self.camp)
.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")
)
@ -1327,7 +1331,7 @@ class UrlDeleteView(
class FeedbackRedirectView(LoginRequiredMixin, EventViewMixin, DetailView):
""" Redirect to the appropriate view """
"""Redirect to the appropriate view"""
model = models.Event
slug_url_kwarg = "event_slug"

View File

@ -11,6 +11,8 @@ class Migration(migrations.Migration):
operations = [
migrations.RenameField(
model_name="ride", old_name="location", new_name="from_location",
model_name="ride",
old_name="location",
new_name="from_location",
),
]

View File

@ -1,5 +1,6 @@
from django.db import models
from django.urls import reverse
from utils.models import CampRelatedModel, UUIDModel

View File

@ -1,4 +1,3 @@
from camps.mixins import CampViewMixin
from django import forms
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
@ -11,6 +10,8 @@ from django.views.generic import (
ListView,
UpdateView,
)
from camps.mixins import CampViewMixin
from teams.models import Team
from utils.email import add_outgoing_email
from utils.mixins import UserIsObjectOwnerMixin

View File

@ -1,4 +1,5 @@
from django.contrib import admin
from tickets.admin import ShopTicketInline
from .models import (

View File

@ -3,6 +3,7 @@ import logging
import requests
from django.conf import settings
from vendor.coinify.coinify_api import CoinifyAPI
from .models import CoinifyAPICallback, CoinifyAPIInvoice, CoinifyAPIRequest

View File

@ -2,6 +2,7 @@ import factory
from django.utils import timezone
from factory.django import DjangoModelFactory
from psycopg2.extras import DateTimeTZRange
from utils.factories import UserFactory

View File

@ -1,5 +1,6 @@
from django import forms
from django.forms import modelformset_factory
from shop.models import OrderProductRelation

View File

@ -3,6 +3,7 @@ import logging
from django.conf import settings
from django.core.files import File
from django.db.models import Q
from shop.email import add_creditnote_email, add_invoice_email
from shop.models import CreditNote, CustomOrder, Invoice, Order
from utils.pdf import generate_pdf_letter
@ -13,10 +14,10 @@ logger = logging.getLogger("bornhack.%s" % __name__)
def do_work():
"""
The invoice worker creates Invoice objects for shop orders and
for custom orders. It also generates PDF files for Invoice objects
that have no PDF. It also emails invoices for shop orders.
It also generates proforma invoices for all closed orders.
The invoice worker creates Invoice objects for shop orders and
for custom orders. It also generates PDF files for Invoice objects
that have no PDF. It also emails invoices for shop orders.
It also generates proforma invoices for all closed orders.
"""
# check if we need to generate any proforma invoices for shop orders

View File

@ -11,7 +11,9 @@ class Migration(migrations.Migration):
operations = [
migrations.AlterField(
model_name="coinifyapicallback", name="headers", field=models.JSONField(),
model_name="coinifyapicallback",
name="headers",
field=models.JSONField(),
),
migrations.AlterField(
model_name="coinifyapicallback",
@ -24,13 +26,19 @@ class Migration(migrations.Migration):
field=models.JSONField(),
),
migrations.AlterField(
model_name="coinifyapirequest", name="payload", field=models.JSONField(),
model_name="coinifyapirequest",
name="payload",
field=models.JSONField(),
),
migrations.AlterField(
model_name="coinifyapirequest", name="response", field=models.JSONField(),
model_name="coinifyapirequest",
name="response",
field=models.JSONField(),
),
migrations.AlterField(
model_name="epaycallback", name="payload", field=models.JSONField(),
model_name="epaycallback",
name="payload",
field=models.JSONField(),
),
migrations.AlterField(
model_name="order",

View File

@ -13,6 +13,7 @@ from django.utils import timezone
from django.utils.dateparse import parse_datetime
from django.utils.translation import ugettext_lazy as _
from unidecode import unidecode
from utils.models import CreatedUpdatedModel, UUIDModel
from utils.slugs import unique_slugify
@ -160,7 +161,7 @@ class Order(CreatedUpdatedModel):
return False
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 (
hasattr(settings, "COINIFY_CALLBACK_HOSTNAME")
and settings.COINIFY_CALLBACK_HOSTNAME

View File

@ -2,6 +2,7 @@ from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from psycopg2.extras import DateTimeTZRange
from shop.forms import OrderProductRelationForm
from tickets.factories import TicketTypeFactory
from tickets.models import ShopTicket
@ -11,16 +12,16 @@ from .factories import OrderFactory, OrderProductRelationFactory, ProductFactory
class ProductAvailabilityTest(TestCase):
""" Test logic about availability of products. """
"""Test logic about availability of products."""
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)
self.assertEqual(product.left_in_stock, 10)
self.assertTrue(product.is_available())
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)
OrderProductRelationFactory(product=product, order__open=None)
@ -39,14 +40,14 @@ class ProductAvailabilityTest(TestCase):
self.assertTrue(product.is_available())
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()
# The factory defines the timeframe as now and 31 days forward.
self.assertTrue(product.is_time_available)
self.assertTrue(product.is_available())
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(
lower=timezone.now() - timezone.timedelta(5),
upper=timezone.now() - timezone.timedelta(1),
@ -57,7 +58,7 @@ class ProductAvailabilityTest(TestCase):
self.assertFalse(product.is_available())
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))
product = ProductFactory(available_in=available_in)
# Make sure there is no upper - just in case.
@ -67,7 +68,7 @@ class ProductAvailabilityTest(TestCase):
self.assertFalse(product.is_available())
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))
product = ProductFactory(available_in=available_in)
# Make sure there is no upper - just in case.

View File

@ -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.base import RedirectView
from django.views.generic.detail import SingleObjectMixin
from shop.models import (
CreditNote,
EpayCallback,

View File

@ -2,6 +2,7 @@ import logging
import os
from django.conf import settings
from teams.models import Team
from utils.email import add_outgoing_email

View File

@ -1,9 +1,10 @@
# coding: utf-8
import logging
from camps.models import Camp
from django.core.management.base import BaseCommand
from django.utils import timezone
from camps.models import Camp
from sponsors.email import add_sponsorticket_email
from sponsors.models import Sponsor

View File

@ -1,7 +1,8 @@
# coding: utf-8
from camps.models import Camp
from django.core.management.base import BaseCommand
from django.utils import timezone
from camps.models import Camp
from sponsors.models import Sponsor
from tickets.models import SponsorTicket, TicketType

View File

@ -2,9 +2,10 @@
# Generated by Django 1.11 on 2017-07-11 21:35
from __future__ import unicode_literals
import sponsors.models
from django.db import migrations, models
import sponsors.models
class Migration(migrations.Migration):

View File

@ -1,4 +1,5 @@
from django.db import models
from utils.models import CampRelatedModel

View File

@ -1,6 +1,7 @@
from camps.mixins import CampViewMixin
from django.views.generic import ListView
from camps.mixins import CampViewMixin
from .models import Sponsor

View File

@ -1,6 +1,7 @@
from camps.utils import CampPropertyListFilter
from django.contrib import admin
from camps.utils import CampPropertyListFilter
from .email import add_added_membership_email, add_removed_membership_email
from .models import Team, TeamMember, TeamShift, TeamTask

View File

@ -5,6 +5,7 @@ from django.contrib.postgres.fields import DateTimeRangeField
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse_lazy
from utils.models import CampRelatedModel, CreatedUpdatedModel, UUIDModel
from utils.slugs import unique_slugify
@ -317,7 +318,7 @@ class TeamMember(CampRelatedModel):
@property
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
camp_filter = "team__camp"
@ -362,7 +363,7 @@ class TeamTask(CampRelatedModel):
@property
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
camp_filter = "team__camp"
@ -406,7 +407,7 @@ class TeamShift(CampRelatedModel):
@property
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
camp_filter = "team__camp"

View File

@ -1,6 +1,5 @@
import logging
logger = logging.getLogger("bornhack.%s" % __name__)
@ -13,6 +12,7 @@ def teammember_saved(sender, instance, created, **kwargs):
# call the mail sending function
# late import to please django 3.2 or "django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet."
from .email import add_new_membership_email
if not add_new_membership_email(instance):
logger.error("Error adding email to outgoing queue")

View File

@ -1,5 +1,6 @@
from django import template
from django.utils.safestring import mark_safe
from teams.models import TeamMember
register = template.Library()

View File

@ -1,4 +1,5 @@
from django.urls import include, path
from teams.views.base import (
FixIrcAclView,
TeamGeneralView,

View File

@ -1,6 +1,5 @@
import logging
from camps.mixins import CampViewMixin
from django.conf import settings
from django.contrib import messages
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.edit import UpdateView
from camps.mixins import CampViewMixin
from ..models import Team, TeamMember
from .mixins import EnsureTeamResponsibleMixin

View File

@ -1,7 +1,8 @@
from camps.mixins import CampViewMixin
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import DetailView
from camps.mixins import CampViewMixin
from ..models import Team, TeamMember

View File

@ -1,10 +1,11 @@
from camps.mixins import CampViewMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
from info.models import InfoCategory, InfoItem
from reversion.views import RevisionMixin
from camps.mixins import CampViewMixin
from info.models import InfoCategory, InfoItem
from ..models import Team
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin

View File

@ -1,10 +1,11 @@
import logging
from camps.mixins import CampViewMixin
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect
from django.views.generic import DetailView, UpdateView
from camps.mixins import CampViewMixin
from profiles.models import Profile
from ..email import add_added_membership_email, add_removed_membership_email

View File

@ -1,6 +1,7 @@
from django.contrib import messages
from django.shortcuts import redirect
from django.views.generic.detail import SingleObjectMixin
from teams.models import Team, TeamMember

View File

@ -1,4 +1,3 @@
from camps.mixins import CampViewMixin
from django import forms
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
@ -17,6 +16,8 @@ from django.views.generic import (
)
from psycopg2.extras import DateTimeTZRange
from camps.mixins import CampViewMixin
from ..models import Team, TeamMember, TeamShift

View File

@ -1,10 +1,11 @@
from camps.mixins import CampViewMixin
from django import forms
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseNotAllowed, HttpResponseRedirect
from django.views.generic import CreateView, DetailView, UpdateView
from camps.mixins import CampViewMixin
from ..models import TaskComment, Team, TeamMember, TeamTask
from .mixins import EnsureTeamResponsibleMixin, TeamViewMixin

Some files were not shown because too many files have changed in this diff Show More