diff --git a/src/camps/migrations/0039_camp_kickoff.py b/src/camps/migrations/0039_camp_kickoff.py index 26d222268..476560237 100644 --- a/src/camps/migrations/0039_camp_kickoff.py +++ b/src/camps/migrations/0039_camp_kickoff.py @@ -1,19 +1,21 @@ # Generated by Django 4.2.21 on 2025-05-28 22:50 +from __future__ import annotations import django.contrib.postgres.fields.ranges from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('camps', '0038_alter_permission_options'), + ("camps", "0038_alter_permission_options"), ] operations = [ migrations.AddField( - model_name='camp', - name='kickoff', - field=django.contrib.postgres.fields.ranges.DateTimeRangeField(blank=True, help_text='The camp kickoff period.', null=True, verbose_name='Camp Kickoff'), + model_name="camp", + name="kickoff", + field=django.contrib.postgres.fields.ranges.DateTimeRangeField( + blank=True, help_text="The camp kickoff period.", null=True, verbose_name="Camp Kickoff" + ), ), ] diff --git a/src/camps/migrations/0040_camp_ticket_type_full_week_adult_and_more.py b/src/camps/migrations/0040_camp_ticket_type_full_week_adult_and_more.py new file mode 100644 index 000000000..dea7193d2 --- /dev/null +++ b/src/camps/migrations/0040_camp_ticket_type_full_week_adult_and_more.py @@ -0,0 +1,64 @@ +# Generated by Django 4.2.21 on 2025-07-01 13:04 +from __future__ import annotations + +import django.db.models.deletion +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + dependencies = [ + ("tickets", "0028_alter_prizeticket_comment"), + ("camps", "0039_camp_kickoff"), + ] + + operations = [ + migrations.AddField( + model_name="camp", + name="ticket_type_full_week_adult", + field=models.ForeignKey( + blank=True, + help_text="The ticket type for 'Adult Full Week' for this camp", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="full_week_adult_camps", + to="tickets.tickettype", + ), + ), + migrations.AddField( + model_name="camp", + name="ticket_type_full_week_child", + field=models.ForeignKey( + blank=True, + help_text="The ticket type for 'Child Full Week' for this camp", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="full_week_child_camps", + to="tickets.tickettype", + ), + ), + migrations.AddField( + model_name="camp", + name="ticket_type_one_day_adult", + field=models.ForeignKey( + blank=True, + help_text="The ticket type for 'Adult One Day' for this camp", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="one_day_adult_camps", + to="tickets.tickettype", + ), + ), + migrations.AddField( + model_name="camp", + name="ticket_type_one_day_child", + field=models.ForeignKey( + blank=True, + help_text="The ticket type for 'Child One Day' for this camp", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="one_day_child_camps", + to="tickets.tickettype", + ), + ), + ] diff --git a/src/camps/models.py b/src/camps/models.py index 9523cbdd0..6c0aa881a 100644 --- a/src/camps/models.py +++ b/src/camps/models.py @@ -2,19 +2,22 @@ import logging from datetime import timedelta -from django.conf import settings from django.apps import apps -from django.contrib.postgres.fields import DateTimeRangeField +from django.conf import settings +from django.contrib.auth.models import Permission as DjangoPermission from django.contrib.contenttypes.models import ContentType +from django.contrib.postgres.fields import DateTimeRangeField from django.core.exceptions import ValidationError -from django.contrib.auth.models import Permission as DjangoPermission from django.db import models from django.urls import reverse from django.utils import timezone from django_prometheus.models import ExportModelOperationsMixin from psycopg2.extras import DateTimeTZRange +from tickets.models import PrizeTicket +from tickets.models import ShopTicket +from tickets.models import SponsorTicket from utils.models import CreatedUpdatedModel from utils.models import UUIDModel @@ -62,7 +65,12 @@ class Meta: help_text="Abbreviated version of the slug. Used in IRC channel names and other places with restricted name length.", ) - kickoff = DateTimeRangeField(null=True, blank=True, verbose_name="Camp Kickoff", help_text="The camp kickoff period.") + kickoff = DateTimeRangeField( + null=True, + blank=True, + verbose_name="Camp Kickoff", + help_text="The camp kickoff period.", + ) buildup = DateTimeRangeField( verbose_name="Buildup Period", @@ -128,6 +136,42 @@ class Meta: related_name="+", ) + ticket_type_full_week_adult = models.ForeignKey( + "tickets.TicketType", + on_delete=models.SET_NULL, + help_text="The ticket type for 'Adult Full Week' for this camp", + null=True, + blank=True, + related_name="full_week_adult_camps", + ) + + ticket_type_one_day_adult = models.ForeignKey( + "tickets.TicketType", + on_delete=models.SET_NULL, + help_text="The ticket type for 'Adult One Day' for this camp", + null=True, + blank=True, + related_name="one_day_adult_camps", + ) + + ticket_type_full_week_child = models.ForeignKey( + "tickets.TicketType", + on_delete=models.SET_NULL, + help_text="The ticket type for 'Child Full Week' for this camp", + null=True, + blank=True, + related_name="full_week_child_camps", + ) + + ticket_type_one_day_child = models.ForeignKey( + "tickets.TicketType", + on_delete=models.SET_NULL, + help_text="The ticket type for 'Child One Day' for this camp", + null=True, + blank=True, + related_name="one_day_child_camps", + ) + def get_absolute_url(self): return reverse("camp_detail", kwargs={"camp_slug": self.slug}) @@ -152,7 +196,6 @@ def clean(self) -> None: def __str__(self) -> str: return f"{self.title} - {self.tagline}" - def activate_team_permissions(self) -> None: """Add permissions to this camps teams.""" permission_content_type = ContentType.objects.get_for_model(Permission) @@ -181,7 +224,6 @@ def deactivate_team_permissions(self) -> None: group.permissions.remove(permission) logger.debug(f"Removed permission {permission} from group {group}") - @property def logo_small(self) -> str: return f"img/{self.slug}/logo/{self.slug}-logo-s.png" @@ -303,3 +345,133 @@ def event_sessions(self): def event_slots(self): EventSlot = apps.get_model("program", "EventSlot") return EventSlot.objects.filter(event_session__in=self.event_sessions.all()) + + @property + def checked_in_full_week_adults(self) -> int: + """Return the count of full week adult tickets checked in""" + shop_tickets = ( + ShopTicket.objects.filter( + ticket_type=self.ticket_type_full_week_adult, + ).exclude(used_at=None) + ).count() + + sponsor_tickets = ( + SponsorTicket.objects.filter( + ticket_type=self.ticket_type_full_week_adult, + ).exclude(used_at=None) + ).count() + + prize_tickets = ( + PrizeTicket.objects.filter( + ticket_type=self.ticket_type_full_week_adult, + ).exclude(used_at=None) + ).count() + + return shop_tickets + sponsor_tickets + prize_tickets + + @property + def checked_in_full_week_children(self) -> int: + """Return the count of full week children tickets checked in""" + shop_tickets = ( + ShopTicket.objects.filter( + ticket_type=self.ticket_type_full_week_child, + ).exclude(used_at=None) + ).count() + + sponsor_tickets = ( + SponsorTicket.objects.filter( + ticket_type=self.ticket_type_full_week_child, + ).exclude(used_at=None) + ).count() + + prize_tickets = ( + PrizeTicket.objects.filter( + ticket_type=self.ticket_type_full_week_child, + ).exclude(used_at=None) + ).count() + + return shop_tickets + sponsor_tickets + prize_tickets + + @property + def checked_in_one_day_adults(self) -> int: + """Return the count of todays one day adult tickets checked in. + + Count tickets with a checked in timestamp from 0600-0600 next day. + Reason being early arriving participants might get checked in before 10. + """ + now = timezone.localtime() + today_06_hour = now.replace(hour=6, minute=0, second=0) + if now < today_06_hour: + start = today_06_hour - timezone.timedelta(days=1) + end = today_06_hour + else: + start = today_06_hour + end = today_06_hour + timezone.timedelta(days=1) + + shop_tickets = ( + ShopTicket.objects.filter( + ticket_type=self.ticket_type_one_day_adult, + ).filter(used_at__gte=start, used_at__lt=end) + ).count() + + sponsor_tickets = ( + SponsorTicket.objects.filter( + ticket_type=self.ticket_type_one_day_adult, + ).filter(used_at__gte=start, used_at__lt=end) + ).count() + + prize_tickets = ( + PrizeTicket.objects.filter( + ticket_type=self.ticket_type_one_day_adult, + ).filter(used_at__gte=start, used_at__lt=end) + ).count() + + return shop_tickets + sponsor_tickets + prize_tickets + + @property + def checked_in_one_day_children(self) -> int: + """Return the count of todays one day children tickets checked in. + + Count tickets with a checked in timestamp from 0600-0600 next day. + Reason being early arriving participants might get checked in before 10. + """ + now = timezone.localtime() + today_06_hour = now.replace(hour=6, minute=0, second=0) + if now < today_06_hour: + start = today_06_hour - timezone.timedelta(days=1) + end = today_06_hour + else: + start = today_06_hour + end = today_06_hour + timezone.timedelta(days=1) + + shop_tickets = ( + ShopTicket.objects.filter( + ticket_type=self.ticket_type_one_day_child, + ).filter(used_at__gte=start, used_at__lt=end) + ).count() + + sponsor_tickets = ( + SponsorTicket.objects.filter( + ticket_type=self.ticket_type_one_day_child, + ).filter(used_at__gte=start, used_at__lt=end) + ).count() + + prize_tickets = ( + PrizeTicket.objects.filter( + ticket_type=self.ticket_type_one_day_child, + ).filter(used_at__gte=start, used_at__lt=end) + ).count() + + return shop_tickets + sponsor_tickets + prize_tickets + + @property + def participant_count(self) -> int: + """Retrieve the participant count for all used 'full week' tickets + and todays used 'one day' tickets. + """ + return ( + self.checked_in_full_week_adults + + self.checked_in_full_week_children + + self.checked_in_one_day_adults + + self.checked_in_one_day_children + ) diff --git a/src/camps/tests.py b/src/camps/tests.py index d9546b6d0..8a1911942 100644 --- a/src/camps/tests.py +++ b/src/camps/tests.py @@ -4,6 +4,13 @@ from django.test import TestCase from django.urls import reverse +from django.utils import timezone + +from sponsors.models import Sponsor +from tickets.models import PrizeTicket +from tickets.models import ShopTicket +from tickets.models import SponsorTicket +from utils.tests import BornhackTestBase class CampMenuTest(TestCase): @@ -15,3 +22,194 @@ def test_this_year_shown_on_homepage(self): year = (datetime.date.today() - datetime.timedelta(days=59)).year href = reverse("camp_detail", kwargs={"camp_slug": f"bornhack-{year}"}) assert href in response.content.decode("utf-8") + + +class TestCampModel(BornhackTestBase): + """Tests for the Camp model""" + + def setUp(self) -> None: + super().setUp() + self.full_week_adults = { # All tickets are created by bootstrapping + "shop_tickets": ( + ShopTicket.objects.filter(ticket_type__camp=self.camp).filter( + ticket_type=self.camp.ticket_type_full_week_adult, + ) + ), + "sponsor_tickets": ( + SponsorTicket.objects.filter(ticket_type__camp=self.camp).filter( + ticket_type=self.camp.ticket_type_full_week_adult, + ) + ), + "prize_tickets": ( + PrizeTicket.objects.filter(ticket_type__camp=self.camp).filter( + ticket_type=self.camp.ticket_type_full_week_adult, + ) + ), + } + self.full_week_children = ( # ShopTickets created by bootstrapping + ShopTicket.objects.filter(ticket_type__camp=self.camp).filter( + ticket_type=self.camp.ticket_type_full_week_child, + ) + ) + self.one_day_adults = ( # ShopTickets created by bootstrapping + ShopTicket.objects.filter(ticket_type__camp=self.camp).filter( + ticket_type=self.camp.ticket_type_one_day_adult, + ) + ) + self.one_day_children = ( # ShopTickets created by bootstrapping + ShopTicket.objects.filter(ticket_type__camp=self.camp).filter( + ticket_type=self.camp.ticket_type_one_day_child, + ) + ) + + def test_checked_in_full_week_adults_all_ticket_types(self) -> None: + """Test the return value of checked in full week adults for all tickets""" + tickets = [ + self.full_week_adults["shop_tickets"][0], + self.full_week_adults["sponsor_tickets"][0], + self.full_week_adults["prize_tickets"][0], + ] + for ticket in tickets: + ticket.used_at = self.camp.camp.lower + ticket.save() + + assert self.camp.checked_in_full_week_adults == 3 + + def test_checked_in_full_week_children_shop_tickets(self) -> None: + """Test the return value of checked in full week children with shop ticket""" + ticket = self.full_week_children[0] + ticket.used_at = self.camp.camp.lower + ticket.save() + assert self.camp.checked_in_full_week_children == 1 + + def test_checked_in_full_week_children_with_sponsor_ticket(self) -> None: + """Test the return value of checked in full week children with sponsor ticket""" + sponsor = Sponsor.objects.all().first() + SponsorTicket.objects.create( + sponsor=sponsor, + ticket_type=self.camp.ticket_type_full_week_child, + used_at=self.camp.camp.lower, + ) + assert self.camp.checked_in_full_week_children == 1 + + def test_checked_in_full_week_children_with_prize_ticket(self) -> None: + """Test the return value of checked in full week children with prize ticket""" + PrizeTicket.objects.create( + user=self.users[0], + ticket_type=self.camp.ticket_type_full_week_child, + comment="Prize winner", + used_at=self.camp.camp.lower, + ) + assert self.camp.checked_in_full_week_children == 1 + + def test_checked_in_one_day_adults(self) -> None: + """Test the return value of checked in one day adults today""" + for ticket in self.one_day_adults[:2]: + ticket.used_at = timezone.localtime() + ticket.save() + + assert self.camp.checked_in_one_day_adults == 2 + + def test_checked_in_one_day_adults_with_sponsor_ticket(self) -> None: + """Test the return value of checked in one day adults + with sponsor ticket today + """ + sponsor = Sponsor.objects.all().first() + SponsorTicket.objects.create( + sponsor=sponsor, + ticket_type=self.camp.ticket_type_one_day_adult, + used_at=timezone.localtime(), + ) + + assert self.camp.checked_in_one_day_adults == 1 + + def test_checked_in_one_day_adults_with_prize_ticket(self) -> None: + """Test the return value of checked in one day adults + with prize ticket today + """ + PrizeTicket.objects.create( + user=self.users[0], + ticket_type=self.camp.ticket_type_one_day_adult, + comment="Prize winner", + used_at=timezone.localtime(), + ) + + assert self.camp.checked_in_one_day_adults == 1 + + def test_checked_in_one_day_adults_timing(self) -> None: + """Test check in before 06 yesterday don't count""" + valid = self.one_day_adults[0] + valid.used_at = timezone.localtime() + valid.save() + + not_valid = self.one_day_adults[1] + not_valid.used_at = timezone.localtime() - timezone.timedelta(days=2) + not_valid.save() + + assert self.camp.checked_in_one_day_adults == 1 + + def test_checked_in_one_day_children(self) -> None: + """Test the return value of checked in one day children today""" + for ticket in self.one_day_children[:2]: + ticket.used_at = timezone.localtime() + ticket.save() + + assert self.camp.checked_in_one_day_children == 2 + + def test_checked_in_one_day_children_with_sponsor_ticket(self) -> None: + """Test the return value of checked in one day children + with sponsor ticket today + """ + sponsor = Sponsor.objects.all().first() + SponsorTicket.objects.create( + sponsor=sponsor, + ticket_type=self.camp.ticket_type_one_day_child, + used_at=timezone.localtime(), + ) + + assert self.camp.checked_in_one_day_children == 1 + + def test_checked_in_one_day_children_with_prize_ticket(self) -> None: + """Test the return value of checked in one day children + with prize ticket today + """ + PrizeTicket.objects.create( + user=self.users[0], + ticket_type=self.camp.ticket_type_one_day_child, + comment="Prize winner", + used_at=timezone.localtime(), + ) + + assert self.camp.checked_in_one_day_children == 1 + + def test_checked_in_one_day_children_timing(self) -> None: + """Test check in before 06 yesterday don't count""" + valid = self.one_day_children[0] + valid.used_at = timezone.localtime() + valid.save() + + not_valid = self.one_day_children[1] + not_valid.used_at = timezone.localtime() - timezone.timedelta(days=2) + not_valid.save() + + assert self.camp.checked_in_one_day_children == 1 + + def test_participant_count(self) -> None: + """Test the count of all participants""" + adult_full_week = self.full_week_adults["shop_tickets"][0] + adult_full_week.used_at = self.camp.camp.lower + adult_full_week.save() + + child_full_week = self.full_week_children[0] + child_full_week.used_at = self.camp.camp.lower + child_full_week.save() + + adult_one_day = self.one_day_adults[0] + adult_one_day.used_at = timezone.localtime() + adult_one_day.save() + + child_one_day = self.one_day_children[0] + child_one_day.used_at = timezone.localtime() + child_one_day.save() + + assert self.camp.participant_count == 4 diff --git a/src/utils/bootstrap/base.py b/src/utils/bootstrap/base.py index ded23177b..2cb76c6e4 100644 --- a/src/utils/bootstrap/base.py +++ b/src/utils/bootstrap/base.py @@ -88,6 +88,8 @@ from teams.models import TeamMember from teams.models import TeamShift from teams.models import TeamTask +from tickets.models import PrizeTicket +from tickets.models import SponsorTicket from tickets.models import TicketType from tokens.models import Token from tokens.models import TokenFind @@ -335,7 +337,12 @@ def create_facilities(self, facility_types: dict) -> dict: ) return facilities - def create_facility_feedbacks(self, facilities: dict, options: dict, users: dict) -> None: + def create_facility_feedbacks( + self, + facilities: dict, + options: dict, + users: dict, + ) -> None: """Create facility feedbacks.""" self.output("Creating facility feedbacks...") FacilityFeedback.objects.create( @@ -599,18 +606,22 @@ def create_camp_ticket_types(self, camp: Camp) -> dict: name="Adult Full Week", camp=camp, ) + camp.ticket_type_full_week_adult = types["adult_full_week"] types["adult_one_day"] = TicketType.objects.create( name="Adult One Day", camp=camp, ) + camp.ticket_type_one_day_adult = types["adult_one_day"] types["child_full_week"] = TicketType.objects.create( name="Child Full Week", camp=camp, ) + camp.ticket_type_full_week_child = types["child_full_week"] types["child_one_day"] = TicketType.objects.create( name="Child One Day", camp=camp, ) + camp.ticket_type_one_day_child = types["child_one_day"] types["village"] = TicketType.objects.create( name="Village", camp=camp, @@ -631,7 +642,12 @@ def create_camp_ticket_types(self, camp: Camp) -> dict: return types - def create_camp_products(self, camp: Camp, categories: dict, ticket_types: dict) -> dict: + def create_camp_products( + self, + camp: Camp, + categories: dict, + ticket_types: dict, + ) -> dict: """Create camp shop products.""" products = {} year = camp.camp.lower.year @@ -675,6 +691,25 @@ def create_camp_products(self, camp: Camp, categories: dict, ticket_types: dict) ticket_type=ticket_types["adult_full_week"], ) + name = f"{camp_prefix} Child Ticket (5-15 year old)" + products["child_ticket"] = Product.objects.create( + name=name, + description="A child ticket", + price=495, + category=categories["tickets"], + available_in=( + datetime(year, 1, 1, 12, 0, tzinfo=tz), + datetime(year, 12, 20, 12, 0, tzinfo=tz), + ), + slug=unique_slugify( + name, + slugs_in_use=Product.objects.filter( + category=categories["tickets"], + ).values_list("slug", flat=True), + ), + ticket_type=ticket_types["child_full_week"], + ) + name = f"{camp_prefix} One day ticket" products["one_day_ticket"] = Product.objects.create( name=name, @@ -694,6 +729,25 @@ def create_camp_products(self, camp: Camp, categories: dict, ticket_types: dict) ticket_type=ticket_types["adult_one_day"], ) + name = f"{camp_prefix} One day ticket child" + products["one_day_ticket_child"] = Product.objects.create( + name=name, + description="One day ticket child", + price=165, + category=categories["tickets"], + available_in=( + datetime(year, 1, 1, 12, 0, tzinfo=tz), + datetime(year, 12, 20, 12, 0, tzinfo=tz), + ), + slug=unique_slugify( + name, + slugs_in_use=Product.objects.filter( + category=categories["tickets"], + ).values_list("slug", flat=True), + ), + ticket_type=ticket_types["child_one_day"], + ) + name = f"{camp_prefix} Village tent 3x3 meters, no floor" products["tent1"] = Product.objects.create( name=name, @@ -899,6 +953,24 @@ def create_orders(self, users: dict, camp_products: dict) -> dict: orders[3].oprs.create(product=camp_products["hax"], quantity=30) orders[3].mark_as_paid(request=None) + orders[4] = Order.objects.create( + user=users[5], + payment_method="in_person", + open=None, + ) + orders[4].oprs.create(product=camp_products["ticket1"], quantity=1) + orders[4].oprs.create(product=camp_products["child_ticket"], quantity=1) + orders[4].mark_as_paid(request=None) + + orders[5] = Order.objects.create( + user=users[6], + payment_method="in_person", + open=None, + ) + orders[5].oprs.create(product=camp_products["one_day_ticket"], quantity=2) + orders[5].oprs.create(product=camp_products["one_day_ticket_child"], quantity=2) + orders[5].mark_as_paid(request=None) + return orders def create_camp_tracks(self, camp: Camp) -> dict: @@ -991,7 +1063,12 @@ def create_camp_news(self, camp: Camp) -> None: published_at=datetime(year, 9, 4, 12, 0, tzinfo=tz), ) - def create_camp_event_sessions(self, camp: Camp, event_types: dict, event_locations: dict) -> None: + def create_camp_event_sessions( + self, + camp: Camp, + event_types: dict, + event_locations: dict, + ) -> None: """Create camp event sessions.""" self.output(f"Creating EventSessions for {camp}...") days = camp.get_days(camppart="camp")[1:-1] @@ -1059,7 +1136,14 @@ def create_camp_event_sessions(self, camp: Camp, event_types: dict, event_locati event_type=event_types["keynote"], event_location=event_locations["speakers_tent"], when=( - datetime(day.lower.year, day.lower.month, day.lower.day, 20, 0, tzinfo=tz), + datetime( + day.lower.year, + day.lower.month, + day.lower.day, + 20, + 0, + tzinfo=tz, + ), datetime( day.lower.year, day.lower.month, @@ -1183,7 +1267,7 @@ class FakeForm: if not data: continue # 90% chance this speaker is available for any given chunk - form.cleaned_data[data["fieldname"]] = random.randint(1, 100) < 90 # noqa: PLR2004, S311 + form.cleaned_data[data["fieldname"]] = random.randint(1, 100) < 90 # print(f"saving availability for speaker {sp}: {form.cleaned_data}") save_speaker_availability(form, sp) @@ -1214,7 +1298,7 @@ def approve_event_proposals(self, camp: Camp) -> None: break else: # all speakers are approved, approve the event? always approve keynotes! - if random.randint(1, 100) < 90 or ep.event_type.name == "Keynote": # noqa: PLR2004, S311 + if random.randint(1, 100) < 90 or ep.event_type.name == "Keynote": ep.mark_as_approved() else: ep.mark_as_rejected() @@ -1459,7 +1543,12 @@ def create_camp_team_tasks(self, camp: Camp, teams: dict) -> None: description="We need ice cubes and crushed ice in the bar", ) - def create_camp_team_memberships(self, camp: Camp, teams: dict, users: dict) -> dict: + def create_camp_team_memberships( + self, + camp: Camp, + teams: dict, + users: dict, + ) -> dict: """Create camp team memberships.""" memberships = {} year = camp.camp.lower.year @@ -1573,7 +1662,12 @@ def create_camp_team_memberships(self, camp: Camp, teams: dict, users: dict) -> ) return memberships - def create_camp_team_shifts(self, camp: Camp, teams: dict, team_memberships: dict) -> None: + def create_camp_team_shifts( + self, + camp: Camp, + teams: dict, + team_memberships: dict, + ) -> None: """Create camp team shifts.""" shifts = {} year = camp.camp.lower.year @@ -1811,45 +1905,88 @@ def create_camp_sponsor_tiers(self, camp: Camp) -> dict: return tiers - def create_camp_sponsors(self, camp: Camp, tiers: dict) -> None: + def create_camp_sponsors(self, camp: Camp, tiers: dict) -> list: """Create the camp sponsors.""" year = camp.camp.lower.year + sponsors = [] self.output(f"Creating sponsors for {year}...") - Sponsor.objects.create( - name="PROSA", - tier=tiers["platinum"], - description="Bus Trip", - logo_filename="PROSA-logo.png", - url="https://www.prosa.dk", - ) - Sponsor.objects.create( - name="DKUUG", - tier=tiers["platinum"], - description="Speakers tent", - logo_filename="DKUUGlogo.jpeg", - url="http://www.dkuug.dk/", - ) - Sponsor.objects.create( - name="LetsGo", - tier=tiers["silver"], - description="Shuttle", - logo_filename="letsgo.png", - url="https://letsgo.dk", - ) - Sponsor.objects.create( - name="Saxo Bank", - tier=tiers["gold"], - description="Cash Sponsorship", - logo_filename="saxobank.png", - url="https://home.saxo", - ) - Sponsor.objects.create( - name="CSIS", - tier=tiers["sponsor"], - description="Cash Sponsorship", - logo_filename="CSIS_PRI_LOGO_TURQUOISE_RGB.jpg", - url="https://csis.dk", + sponsors.append( + Sponsor.objects.create( + name="PROSA", + tier=tiers["platinum"], + description="Bus Trip", + logo_filename="PROSA-logo.png", + url="https://www.prosa.dk", + ), + ) + sponsors.append( + Sponsor.objects.create( + name="DKUUG", + tier=tiers["platinum"], + description="Speakers tent", + logo_filename="DKUUGlogo.jpeg", + url="http://www.dkuug.dk/", + ), + ) + sponsors.append( + Sponsor.objects.create( + name="LetsGo", + tier=tiers["silver"], + description="Shuttle", + logo_filename="letsgo.png", + url="https://letsgo.dk", + ), + ) + sponsors.append( + Sponsor.objects.create( + name="Saxo Bank", + tier=tiers["gold"], + description="Cash Sponsorship", + logo_filename="saxobank.png", + url="https://home.saxo", + ), ) + sponsors.append( + Sponsor.objects.create( + name="CSIS", + tier=tiers["sponsor"], + description="Cash Sponsorship", + logo_filename="CSIS_PRI_LOGO_TURQUOISE_RGB.jpg", + url="https://csis.dk", + ), + ) + + return sponsors + + def create_camp_sponsor_tickets( + self, + camp: Camp, + sponsors: list, + tiers: dict, + ticket_types: dict, + ) -> None: + """Create tickets for camp sponsors""" + year = camp.camp.lower.year + self.output(f"Creating sponsor tickets for {year}...") + for sponsor in sponsors: + if sponsor.tier == tiers["platinum"] or sponsor.tier == tiers["gold"]: + for _ in range(10): + SponsorTicket.objects.create( + sponsor=sponsor, + ticket_type=ticket_types["adult_full_week"], + ) + elif sponsor.tier == tiers["silver"]: + for _ in range(5): + SponsorTicket.objects.create( + sponsor=sponsor, + ticket_type=ticket_types["adult_full_week"], + ) + elif sponsor.tier == tiers["sponsor"]: + for _ in range(2): + SponsorTicket.objects.create( + sponsor=sponsor, + ticket_type=ticket_types["adult_full_week"], + ) def create_camp_tokens(self, camp: Camp) -> dict[Token]: """Create the camp tokens.""" @@ -1901,7 +2038,12 @@ def create_camp_tokens(self, camp: Camp) -> dict[Token]: return tokens - def create_camp_token_finds(self, camp: Camp, tokens: dict[Token], users: dict[User]) -> None: + def create_camp_token_finds( + self, + camp: Camp, + tokens: dict[Token], + users: dict[User], + ) -> None: """Create the camp token finds.""" year = camp.camp.lower.year self.output(f"Creating token finds for {year}...") @@ -1914,6 +2056,16 @@ def create_camp_token_finds(self, camp: Camp, tokens: dict[Token], users: dict[U for i in range(6): TokenFind.objects.create(token=tokens[i], user=users[1]) + def create_prize_ticket(self, camp: Camp, ticket_types: dict) -> None: + """Create prize tickets""" + year = camp.camp.lower.year + self.output(f"Creating prize tickets for {year}...") + PrizeTicket.objects.create( + user=self.users[5], + comment="Prize winner", + ticket_type=ticket_types["adult_full_week"], + ) + def create_camp_expenses(self, camp: Camp) -> None: """Create camp expenses.""" self.output(f"Creating expenses for {camp}...") @@ -2167,9 +2319,27 @@ def bootstrap_tests(self) -> None: self.create_camps(camps) self.create_users(16) self.create_event_types() + self.create_product_categories() teams = {} for camp, read_only in self.camps: year = camp.camp.lower.year + if year <= settings.UPCOMING_CAMP_YEAR: + ticket_types = self.create_camp_ticket_types(camp) + camp_products = self.create_camp_products( + camp, + self.product_categories, + ticket_types, + ) + self.create_orders(self.users, camp_products) + sponsor_tiers = self.create_camp_sponsor_tiers(camp) + camp_sponsors = self.create_camp_sponsors(camp, sponsor_tiers) + self.create_camp_sponsor_tickets( + camp, + camp_sponsors, + sponsor_tiers, + ticket_types, + ) + self.create_prize_ticket(camp, ticket_types) teams[year] = self.create_camp_teams(camp) self.create_camp_team_memberships(camp, teams[year], self.users) @@ -2220,7 +2390,11 @@ def bootstrap_camp(self, options: dict) -> None: self.create_camp_team_tasks(camp, teams) - team_memberships = self.create_camp_team_memberships(camp, teams, self.users) + team_memberships = self.create_camp_team_memberships( + camp, + teams, + self.users, + ) self.create_camp_team_shifts(camp, teams, team_memberships) @@ -2267,7 +2441,11 @@ def bootstrap_camp(self, options: dict) -> None: facilities = self.create_facilities(facility_types) - self.create_facility_feedbacks(facilities, self.quickfeedback_options, self.users) + self.create_facility_feedbacks( + facilities, + self.quickfeedback_options, + self.users, + ) info_categories = self.create_camp_info_categories(camp, teams) @@ -2281,7 +2459,7 @@ def bootstrap_camp(self, options: dict) -> None: sponsor_tiers = self.create_camp_sponsor_tiers(camp) - self.create_camp_sponsors(camp, sponsor_tiers) + camp_sponsors = self.create_camp_sponsors(camp, sponsor_tiers) tokens = self.create_camp_tokens(camp)