diff --git a/alembic/versions/a1e2c51ddaac_multitenancy.py b/alembic/versions/a1e2c51ddaac_multitenancy.py new file mode 100644 index 0000000..400ed4f --- /dev/null +++ b/alembic/versions/a1e2c51ddaac_multitenancy.py @@ -0,0 +1,73 @@ +"""Multitenancy + +Revision ID: a1e2c51ddaac +Revises: b648caf8d8fb +Create Date: 2025-12-12 18:13:56.743605 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a1e2c51ddaac' +down_revision = 'b648caf8d8fb' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('event_brand', + sa.Column('uuid', sa.UUID(), nullable=False), + sa.Column('name', sa.Text(), nullable=False), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_event_brand')), + sa.UniqueConstraint('uuid', name=op.f('uq_event_brand_uuid')) + ) + op.add_column('crew', sa.Column('event_brand', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_crew_event_brand_event_brand'), 'crew', 'event_brand', ['event_brand'], ['uuid']) + op.add_column('event', sa.Column('event_brand_uuid', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_event_event_brand_uuid_event_brand'), 'event', 'event_brand', ['event_brand_uuid'], ['uuid']) + op.add_column('location', sa.Column('event_brand_uuid', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_location_event_brand_uuid_event_brand'), 'location', 'event_brand', ['event_brand_uuid'], ['uuid']) + op.add_column('position', sa.Column('event_brand_uuid', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_position_event_brand_uuid_event_brand'), 'position', 'event_brand', ['event_brand_uuid'], ['uuid']) + op.add_column('seatmap', sa.Column('event_brand_uuid', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_seatmap_event_brand_uuid_event_brand'), 'seatmap', 'event_brand', ['event_brand_uuid'], ['uuid']) + op.add_column('seatmap_background', sa.Column('event_brand_uuid', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_seatmap_background_event_brand_uuid_event_brand'), 'seatmap_background', 'event_brand', ['event_brand_uuid'], ['uuid']) + op.add_column('store_session', sa.Column('event_uuid', sa.UUID(), nullable=False)) + op.create_foreign_key(op.f('fk_store_session_event_uuid_event'), 'store_session', 'event', ['event_uuid'], ['uuid']) + op.add_column('ticket_type', sa.Column('event_brand_uuid', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_ticket_type_event_brand_uuid_event_brand'), 'ticket_type', 'event_brand', ['event_brand_uuid'], ['uuid']) + op.add_column('ticket_voucher', sa.Column('event_brand_uuid', sa.UUID(), nullable=False)) + op.create_foreign_key(op.f('fk_ticket_voucher_event_brand_uuid_event_brand'), 'ticket_voucher', 'event_brand', ['event_brand_uuid'], ['uuid']) + op.add_column('user_positions', sa.Column('event_brand_uuid', sa.UUID(), nullable=True)) + op.create_foreign_key(op.f('fk_user_positions_event_brand_uuid_event_brand'), 'user_positions', 'event_brand', ['event_brand_uuid'], ['uuid']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(op.f('fk_user_positions_event_brand_uuid_event_brand'), 'user_positions', type_='foreignkey') + op.drop_column('user_positions', 'event_brand_uuid') + op.drop_constraint(op.f('fk_ticket_voucher_event_brand_uuid_event_brand'), 'ticket_voucher', type_='foreignkey') + op.drop_column('ticket_voucher', 'event_brand_uuid') + op.drop_constraint(op.f('fk_ticket_type_event_brand_uuid_event_brand'), 'ticket_type', type_='foreignkey') + op.drop_column('ticket_type', 'event_brand_uuid') + op.drop_constraint(op.f('fk_store_session_event_uuid_event'), 'store_session', type_='foreignkey') + op.drop_column('store_session', 'event_uuid') + op.drop_constraint(op.f('fk_seatmap_background_event_brand_uuid_event_brand'), 'seatmap_background', type_='foreignkey') + op.drop_column('seatmap_background', 'event_brand_uuid') + op.drop_constraint(op.f('fk_seatmap_event_brand_uuid_event_brand'), 'seatmap', type_='foreignkey') + op.drop_column('seatmap', 'event_brand_uuid') + op.drop_constraint(op.f('fk_position_event_brand_uuid_event_brand'), 'position', type_='foreignkey') + op.drop_column('position', 'event_brand_uuid') + op.drop_constraint(op.f('fk_location_event_brand_uuid_event_brand'), 'location', type_='foreignkey') + op.drop_column('location', 'event_brand_uuid') + op.drop_constraint(op.f('fk_event_event_brand_uuid_event_brand'), 'event', type_='foreignkey') + op.drop_column('event', 'event_brand_uuid') + op.drop_constraint(op.f('fk_crew_event_brand_event_brand'), 'crew', type_='foreignkey') + op.drop_column('crew', 'event_brand') + op.drop_table('event_brand') + # ### end Alembic commands ### diff --git a/phoenixRest/models/core/event.py b/phoenixRest/models/core/event.py index ae980f4..e4394a3 100644 --- a/phoenixRest/models/core/event.py +++ b/phoenixRest/models/core/event.py @@ -8,7 +8,8 @@ Integer, Boolean, Enum, - Table + Table, + func ) from sqlalchemy.dialects.postgresql import UUID @@ -17,6 +18,7 @@ from phoenixRest.models import Base +from phoenixRest.models.core.event_brand import EventBrand from phoenixRest.models.core.user import User from phoenixRest.models.tickets.ticket import Ticket from phoenixRest.models.tickets.ticket_type import TicketType @@ -41,6 +43,9 @@ class Event(Base): __tablename__ = "event" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + event_brand_uuid = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event_brand = relationship("EventBrand", back_populates="events") + booking_time = Column(DateTime, nullable=False) # Delta in seconds from booking time @@ -69,7 +74,7 @@ class Event(Base): cancellation_reason = Column(Text) - def __init__(self, name: str, start_time: DateTime, end_time: DateTime, max_participants: int): + def __init__(self, name: str, start_time: DateTime, end_time: DateTime, max_participants: int, event_brand): self.name = name self.start_time = start_time self.end_time = end_time @@ -77,12 +82,14 @@ def __init__(self, name: str, start_time: DateTime, end_time: DateTime, max_part self.priority_seating_time_delta = 60*30 self.seating_time_delta = 60*60 self.max_participants = max_participants + self.event_brand = event_brand def __json__(self, request): return { 'name': str(self.name), 'uuid': str(self.uuid), + 'event_brand_uuid': str(self.event_brand_uuid), 'participant_age_limit_inclusive': self.participant_age_limit_inclusive, 'crew_age_limit_inclusive': self.crew_age_limit_inclusive, 'booking_time': int(self.booking_time.timestamp()), @@ -109,10 +116,25 @@ def get_total_ticket_availability(self, request): )) \ .count() -def get_current_event(request): - firstEvent = request.db.query(Event).filter(Event.end_time > datetime.now()).order_by(Event.start_time.asc()).first() +def get_current_events(db): + """Returns the uuid of all active events, one per brand. + We use uuid as it is more useful""" + ranked_sub = db.query( + Event.uuid.label("uuid"), + func.row_number().over(partition_by=Event.event_brand, order_by=Event.end_time.asc()).label("rank") + ).filter(Event.end_time > datetime.now()).subquery() + + current_events = list(map(lambda row: row[0], db.query(ranked_sub.c.uuid).filter(ranked_sub.c.rank == 1).all())) + log.info(f"Current events: {current_events}") + + return current_events + + +def get_current_event(db, brand: "Brand"): + """Returns the current event for a given brand""" + firstEvent = db.query(Event).filter(Event.end_time > datetime.now(), Event.event_brand_uuid == brand.uuid).order_by(Event.start_time.asc()).first() if firstEvent is None: - logging.warning("There are no new events") + logging.warning(f"There are no new events for brand {brand.uuid}") return None else: # TODO we want to return ticket types some time? Maybe? diff --git a/phoenixRest/models/core/event_brand.py b/phoenixRest/models/core/event_brand.py new file mode 100644 index 0000000..925d914 --- /dev/null +++ b/phoenixRest/models/core/event_brand.py @@ -0,0 +1,41 @@ +from sqlalchemy import ( + Column, + DateTime, + ForeignKey, + Integer, + UniqueConstraint, + Text, + Integer, + Boolean, + Enum, + Table +) +from sqlalchemy.dialects.postgresql import UUID + +from sqlalchemy.orm import relationship +from sqlalchemy import and_ + +from phoenixRest.models import Base + +import logging +log = logging.getLogger(__name__) + +import uuid + +class EventBrand(Base): + __tablename__ = "event_brand" + + uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + + events = relationship("Event", back_populates="event_brand") + + name = Column(Text, nullable=False) + + def __init__(self, name: str): + self.name = name + + def __json__(self, request): + return { + 'uuid': str(self.uuid), + 'name': str(self.name) + } diff --git a/phoenixRest/models/core/location.py b/phoenixRest/models/core/location.py index 403fe64..832d0cd 100644 --- a/phoenixRest/models/core/location.py +++ b/phoenixRest/models/core/location.py @@ -13,6 +13,8 @@ ) from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship + from phoenixRest.models import Base import uuid @@ -21,6 +23,9 @@ class Location(Base): __tablename__ = "location" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + event_brand_uuid = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event_brand = relationship("EventBrand") + name = Column(Text, nullable=False) address = Column(Text, nullable=False) @@ -31,6 +36,7 @@ def __init__(self, name: str, address: str): def __json__(self, request): return { 'uuid': str(self.uuid), + 'event_brand_uuid': str(self.event_brand_uuid), 'name': self.name, 'address': self.address, } \ No newline at end of file diff --git a/phoenixRest/models/crew/crew.py b/phoenixRest/models/crew/crew.py index 2c36a59..05719d5 100644 --- a/phoenixRest/models/crew/crew.py +++ b/phoenixRest/models/crew/crew.py @@ -17,6 +17,7 @@ from phoenixRest.models import Base +from phoenixRest.models.core.event_brand import EventBrand from phoenixRest.models.core.user import User from datetime import datetime, timedelta @@ -27,7 +28,10 @@ class Crew(Base): __tablename__ = "crew" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) - + + event_brand = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event = relationship("EventBrand") + name = Column(Text, nullable=False) description = Column(Text, nullable=False) @@ -49,6 +53,7 @@ def __init__(self, name: str, description: str): def __json__(self, request): return { 'uuid': str(self.uuid), + 'event_brand_uuid': str(self.event_brand_uuid), 'name': self.name, 'description': self.description, 'active': self.active, diff --git a/phoenixRest/models/crew/position.py b/phoenixRest/models/crew/position.py index aeb06ef..fdd711f 100644 --- a/phoenixRest/models/crew/position.py +++ b/phoenixRest/models/crew/position.py @@ -25,6 +25,9 @@ class Position(Base): __tablename__ = "position" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + event_brand_uuid = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event_brand = relationship("EventBrand") + crew_uuid = Column(UUID(as_uuid=True), ForeignKey("crew.uuid"), nullable=True) crew = relationship("Crew") @@ -49,6 +52,7 @@ def __init__(self, name: str, description: str): def __json__(self, request): return { 'uuid': str(self.uuid), + 'event_brand_uuid': str(self.event_brand_uuid), 'name': self.name, 'description': self.description, 'is_vanity': self.is_vanity, diff --git a/phoenixRest/models/crew/position_mapping.py b/phoenixRest/models/crew/position_mapping.py index 84ed28a..03e4785 100644 --- a/phoenixRest/models/crew/position_mapping.py +++ b/phoenixRest/models/crew/position_mapping.py @@ -22,6 +22,9 @@ class PositionMapping(Base): __tablename__ = "user_positions" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + event_brand_uuid = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event_brand = relationship("EventBrand") + position_uuid = Column(UUID(as_uuid=True), ForeignKey("position.uuid"), nullable=False) position = relationship("Position", back_populates="position_mappings", uselist=False) @@ -46,6 +49,7 @@ def __init__(self, user, position, event=None): def __json__(self, request): return { 'uuid': self.uuid, + 'event_brand_uuid': str(self.event_brand_uuid), 'position_uuid': self.position_uuid, 'user_uuid': self.user_uuid, 'event_uuid': self.event_uuid, diff --git a/phoenixRest/models/tickets/seatmap.py b/phoenixRest/models/tickets/seatmap.py index 4010bc2..cf6f578 100644 --- a/phoenixRest/models/tickets/seatmap.py +++ b/phoenixRest/models/tickets/seatmap.py @@ -29,6 +29,9 @@ class Seatmap(Base): __tablename__ = "seatmap" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + event_brand_uuid = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event_brand = relationship("EventBrand") + name = Column(Text, nullable=False) description = Column(Text, nullable=False) @@ -50,6 +53,7 @@ def __init__(self, name: str, description: str): def __json__(self, request): return { 'uuid': str(self.uuid), + 'event_brand_uuid': str(self.event_brand_uuid), 'name': str(self.name), 'description': str(self.description), 'background': map_seatmap_background_no_metadata(self.background, request) if self.background is not None else None, diff --git a/phoenixRest/models/tickets/seatmap_background.py b/phoenixRest/models/tickets/seatmap_background.py index b7a8ea5..46947bd 100644 --- a/phoenixRest/models/tickets/seatmap_background.py +++ b/phoenixRest/models/tickets/seatmap_background.py @@ -24,6 +24,9 @@ class SeatmapBackground(Base): __tablename__ = "seatmap_background" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + event_brand_uuid = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event_brand = relationship("EventBrand") + created = Column(DateTime, nullable=False, server_default='NOW()') extension = Column(Text, nullable=False) @@ -47,6 +50,7 @@ def get_fs_location(self, request): def __json__(self, request): return { 'uuid': str(self.uuid), + 'event_brand_uuid': str(self.event_brand_uuid), 'uploader_uuid': self.uploader_uuid, 'created': int(self.created.timestamp()), 'url': self.get_url(request) diff --git a/phoenixRest/models/tickets/store_session.py b/phoenixRest/models/tickets/store_session.py index 70292fc..b778372 100644 --- a/phoenixRest/models/tickets/store_session.py +++ b/phoenixRest/models/tickets/store_session.py @@ -29,6 +29,9 @@ class StoreSession(Base): user_uuid = Column(UUID(as_uuid=True), ForeignKey("user.uuid"), nullable=False) user = relationship("User") + event_uuid = Column(UUID(as_uuid=True), ForeignKey("event.uuid"), nullable=False) + event = relationship("Event") + payment = relationship("Payment", back_populates="store_session") cart_entries = relationship("StoreSessionCartEntry", back_populates="store_session") @@ -36,8 +39,9 @@ class StoreSession(Base): created = Column(DateTime, nullable=False) expires = Column(DateTime, nullable=False) - def __init__(self, user: User, expiry: int): + def __init__(self, user: User, expiry: int, event): self.user = user + self.event = event self.created = datetime.now() self.expires = self.created + timedelta(seconds=expiry) @@ -51,6 +55,7 @@ def __json__(self, request): return { 'uuid': str(self.uuid), 'user_uuid': self.user_uuid, + 'event_uuid': self.event_uuid, 'entries': self.cart_entries, 'total': self.get_total(), 'created': int(self.created.timestamp()), diff --git a/phoenixRest/models/tickets/ticket_type.py b/phoenixRest/models/tickets/ticket_type.py index 98c794c..9d01ee0 100644 --- a/phoenixRest/models/tickets/ticket_type.py +++ b/phoenixRest/models/tickets/ticket_type.py @@ -12,6 +12,8 @@ ) from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship + from phoenixRest.models import Base import uuid @@ -20,6 +22,9 @@ class TicketType(Base): __tablename__ = "ticket_type" uuid = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False) + event_brand_uuid = Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=True) + event_brand = relationship("EventBrand") + name = Column(Text, nullable=False) price = Column(Integer, nullable=False) @@ -45,6 +50,7 @@ def __init__(self, name: str, price: int, description: str, refundable: bool, se def __json__(self, request): return { 'uuid': str(self.uuid), + 'event_brand_uuid': str(self.event_brand_uuid), 'name': self.name, 'price': self.price, 'refundable': self.refundable, diff --git a/phoenixRest/models/tickets/ticket_voucher.py b/phoenixRest/models/tickets/ticket_voucher.py index cd3070a..2d8e6a9 100644 --- a/phoenixRest/models/tickets/ticket_voucher.py +++ b/phoenixRest/models/tickets/ticket_voucher.py @@ -33,6 +33,9 @@ class TicketVoucher(Base): ticket_type_uuid = Column(UUID(as_uuid=True), ForeignKey("ticket_type.uuid"), nullable=False) ticket_type = relationship("TicketType") + + event_brand_uuid= Column(UUID(as_uuid=True), ForeignKey("event_brand.uuid"), nullable=False) + event_brand = relationship("EventBrand") # The ticket once the ticket voucher is generated ticket_id = Column(Integer, ForeignKey("ticket.ticket_id"), nullable=True) @@ -46,12 +49,14 @@ class TicketVoucher(Base): created = Column(DateTime, nullable=False) - def __init__(self, source_user, recipient_user, ticket_type, last_use_event): + def __init__(self, source_user, recipient_user, ticket_type, brand, last_use_event): self.source_user = source_user self.recipient_user = recipient_user self.ticket_type = ticket_type + self.event_brand = brand + self.last_use_event = last_use_event self.created = datetime.now() diff --git a/phoenixRest/tests/conftest.py b/phoenixRest/tests/conftest.py index bf1edb4..7b7d593 100644 --- a/phoenixRest/tests/conftest.py +++ b/phoenixRest/tests/conftest.py @@ -1,3 +1,4 @@ +from phoenixRest.models.core.event_brand import EventBrand from pyramid.paster import get_appsettings import pytest import transaction @@ -46,25 +47,71 @@ def testapp(app, tm, db): }) @pytest.fixture -def upcoming_event(db): +def event_brand(db): + """Creates an event brand that an event can be associated with""" + brand = EventBrand("Event brand!") + db.add(brand) + db.flush() + return brand + +@pytest.fixture +def admin_token(testapp): + privileged_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') + return privileged_token + +@pytest.fixture +def upcoming_event(db, event_brand): """Creates an event that has not yet happened, but where ticketsale hasn't started yet""" event_start = datetime.now() + timedelta(days=62) event_end = datetime.now() + timedelta(days=65) - e = Event("Test event", event_start, event_end, 400) + e = Event("Test event", event_start, event_end, 400, event_brand) db.add(e) db.flush() return e @pytest.fixture -def ticketsale_ongoing_event(db): +def ticketsale_ongoing_event(db, event_brand): """Creates an event that has not yet happened, where the ticket sale is currently ongoing""" event_start = datetime.now() + timedelta(days=10) event_end = datetime.now() + timedelta(days=13) - e = Event("Test event(Ticket sale ongoing)", event_start, event_end, 400) + e = Event("Test event(Ticket sale ongoing)", event_start, event_end, 400, event_brand) db.add(e) db.flush() - return e \ No newline at end of file + return e + +@pytest.fixture +def ongoing_ticket_types(db, testapp, ticketsale_ongoing_event, admin_token): + """Adds existing ticket types to the current event (ticketsale ongoing)""" + + all_ticket_types = testapp.get('/ticketType', headers=dict({ + 'Authorization': "Bearer " + admin_token + }), status=200).json_body + + for ticket_type in all_ticket_types: + testapp.put_json('/event/%s/ticketType' % ticketsale_ongoing_event.uuid, dict({ + 'ticket_type_uuid': ticket_type['uuid'] + }), headers=dict({ + 'Authorization': "Bearer " + admin_token + }), status=200) + + +@pytest.fixture +def ticket_types(db, testapp, upcoming_event, admin_token): + """Adds existing ticket types to the _upcoming event_ (sale not ongoing)""" + + all_ticket_types = testapp.get('/ticketType', headers=dict({ + 'Authorization': "Bearer " + admin_token + }), status=200).json_body + + for ticket_type in all_ticket_types: + testapp.put_json('/event/%s/ticketType' % upcoming_event.uuid, dict({ + 'ticket_type_uuid': ticket_type['uuid'] + }), headers=dict({ + 'Authorization': "Bearer " + admin_token + }), status=200) + + \ No newline at end of file diff --git a/phoenixRest/tests/features/current_event.py b/phoenixRest/tests/features/current_event.py new file mode 100644 index 0000000..56c2088 --- /dev/null +++ b/phoenixRest/tests/features/current_event.py @@ -0,0 +1,53 @@ +from phoenixRest.models.core.event import get_current_event, get_current_events, Event + +from datetime import datetime, timedelta + +def test_get_current_event(db, upcoming_event, event_brand): + """Tests get_current_event""" + current_event = get_current_event(db, event_brand) + assert current_event == upcoming_event + +def test_get_current_events_no_event(db, event_brand): + """Tests that if only the event_brand fixture is included, no event should exist in the system. + This is mostly just a sanity check""" + + current_events = get_current_events(db) + assert len(current_events) == 0 + +def test_get_current_events(db, upcoming_event): + """Tests get_current_event""" + current_events = get_current_events(db) + + current_event_uuids = list(map(lambda u: str(u), current_events)) + assert str(upcoming_event.uuid) in current_event_uuids + + +def test_get_current_events_multiple_upcoming(db, upcoming_event, event_brand): + """Test that if i create a more recent event, it will be the current one""" + + earlier_event = Event("Earlier event", datetime.now() + timedelta(days=20), datetime.now() + timedelta(days=22), 400, event_brand) + earlier_event.booking_time = datetime.now() + timedelta(days=2) + earlier_event.seating_time_delta = 30 + db.add(earlier_event) + db.flush() + + + current_events = get_current_events(db) + + current_event_uuids = list(map(lambda u: str(u), current_events)) + assert str(earlier_event.uuid) in current_event_uuids + +def test_get_current_events_previous(db, upcoming_event, event_brand): + """Test that previous events aren't included""" + + earlier_event = Event("Earlier event", datetime.now() - timedelta(days=20), datetime.now() - timedelta(days=22), 400, event_brand) + earlier_event.booking_time = datetime.now() + timedelta(days=2) + earlier_event.seating_time_delta = 30 + db.add(earlier_event) + db.flush() + + + current_events = get_current_events(db) + + current_event_uuids = list(map(lambda u: str(u), current_events)) + assert str(upcoming_event.uuid) in current_event_uuids \ No newline at end of file diff --git a/phoenixRest/tests/features/vipps/test_payment_str.py b/phoenixRest/tests/features/vipps/test_payment_str.py index a1a6901..e0099ed 100644 --- a/phoenixRest/tests/features/vipps/test_payment_str.py +++ b/phoenixRest/tests/features/vipps/test_payment_str.py @@ -8,9 +8,9 @@ from phoenixRest.models.core.user import User -def test_normal_payment_singular_str(db, testapp): +def test_normal_payment_singular_str(db, testapp, upcoming_event): user = db.query(User).filter(User.firstname == "Jeff").first() - store_session = StoreSession(user, 1000) + store_session = StoreSession(user, 1000, upcoming_event) # Get some ticket types vestibyle = db.query(TicketType).filter(TicketType.name == "Vestibyle").first() @@ -19,7 +19,7 @@ def test_normal_payment_singular_str(db, testapp): store_session.cart_entries.append(StoreSessionCartEntry(vestibyle, 1)) # Create payment - payment = Payment(user, PaymentProvider.vipps, store_session.get_total(), testapp.get_current_event(db)) + payment = Payment(user, PaymentProvider.vipps, store_session.get_total(), upcoming_event) # A payment is added later and need not be bound to store session apparently. # TODO: Maybe store session should be in the constructor? payment.store_session = store_session @@ -29,9 +29,9 @@ def test_normal_payment_singular_str(db, testapp): assert payment_str == "1 Vestibyle-billett" -def test_normal_payment_plural_str(db, testapp): +def test_normal_payment_plural_str(db, testapp, upcoming_event): user = db.query(User).filter(User.firstname == "Jeff").first() - store_session = StoreSession(user, 1000) + store_session = StoreSession(user, 1000, upcoming_event) # Get some ticket types vestibyle = db.query(TicketType).filter(TicketType.name == "Vestibyle").first() @@ -40,7 +40,7 @@ def test_normal_payment_plural_str(db, testapp): store_session.cart_entries.append(StoreSessionCartEntry(vestibyle, 2)) # Create payment - payment = Payment(user, PaymentProvider.vipps, store_session.get_total(), testapp.get_current_event(db)) + payment = Payment(user, PaymentProvider.vipps, store_session.get_total(), upcoming_event) # A payment is added later and need not be bound to store session apparently. # TODO: Maybe store session should be in the constructor? payment.store_session = store_session @@ -50,9 +50,9 @@ def test_normal_payment_plural_str(db, testapp): assert payment_str == "2 Vestibyle-billetter" -def test_long_payment_str(db, testapp): +def test_long_payment_str(db, testapp, upcoming_event): user = db.query(User).filter(User.firstname == "Jeff").first() - store_session = StoreSession(user, 1000) + store_session = StoreSession(user, 1000, upcoming_event) # Get some ticket types vestibyle = db.query(TicketType).filter(TicketType.name == "Vestibyle").first() @@ -68,7 +68,7 @@ def test_long_payment_str(db, testapp): store_session.cart_entries.append(StoreSessionCartEntry(weirdType, 7)) # Create payment - payment = Payment(user, PaymentProvider.vipps, store_session.get_total(), testapp.get_current_event(db)) + payment = Payment(user, PaymentProvider.vipps, store_session.get_total(), upcoming_event) # A payment is added later and need not be bound to store session apparently. # TODO: Maybe store session should be in the constructor? payment.store_session = store_session diff --git a/phoenixRest/tests/views/agenda/__init__.py b/phoenixRest/tests/views/agenda/__init__.py index c09b02e..a4e2720 100644 --- a/phoenixRest/tests/views/agenda/__init__.py +++ b/phoenixRest/tests/views/agenda/__init__.py @@ -3,8 +3,8 @@ import logging # Test getting the current event -def test_get_agenda(testapp): - return testapp.get('/agenda/', status=200) +def test_get_agenda(testapp, upcoming_event): + return testapp.get(f"/event/{upcoming_event.uuid}/agenda", status=200) def test_create_modify_delete_agenda(testapp, upcoming_event): @@ -20,13 +20,12 @@ def test_create_modify_delete_agenda(testapp, upcoming_event): unprivileged_token, refresh = testapp.auth_get_tokens('jeff@example.com', 'sixcharacters') # Attempt to get current event - current_event = testapp.get('/event/current', status=200) - assert current_event.json_body['uuid'] is not None + current_event_uuid = str(upcoming_event.uuid) ### Test to create a new agenda entry as an admin (privileged) and as a regular user (unprivileged) # Attempt to create an agenda entry as an admin (Expects 200) privileged_entry = testapp.put_json('/agenda', dict({ - 'event_uuid': current_event.json_body['uuid'], + 'event_uuid': current_event_uuid, 'title': "Test agenda entry as privileged", 'description': "Test description", 'location': "Test location", @@ -39,7 +38,7 @@ def test_create_modify_delete_agenda(testapp, upcoming_event): # Attempt to create an agenda entry as a regular user (Expects 403) unprivileged_entry = testapp.put_json('/agenda', dict({ - 'event_uuid': current_event.json_body['uuid'], + 'event_uuid': current_event_uuid, 'title': "Test agenda entry as unprivileged", 'description': "Test description", 'location': "Test location", @@ -56,7 +55,7 @@ def test_create_modify_delete_agenda(testapp, upcoming_event): ### Test to modify a newly created agenda entry as an admin (privileged) and as a regular user (unprivileged) # Attempt to modify the newly created agenda entry as an admin, set everything (Expects 200) privileged_modification_setall = testapp.patch_json('/agenda/' + privileged_entry.json_body['uuid'], dict({ - 'event_uuid': current_event.json_body['uuid'], + 'event_uuid': current_event_uuid, 'title': "Modified the title", 'description': "Modified the description", 'location': "Modified the location", @@ -74,7 +73,7 @@ def test_create_modify_delete_agenda(testapp, upcoming_event): # Attempt to modify the newly created agenda entry as an admin, reset everything (Expects 200) privileged_modification_resetall = testapp.patch_json('/agenda/' + privileged_entry.json_body['uuid'], dict({ - 'event_uuid': current_event.json_body['uuid'], + 'event_uuid': current_event_uuid, 'title': "Default title", 'description': "Default description", 'location': "Default location", @@ -92,7 +91,7 @@ def test_create_modify_delete_agenda(testapp, upcoming_event): # Attempt to modify the newly created agenda entry as a regular user, set everything (Expects 403) unprivileged_modification_setall = testapp.patch_json('/agenda/' + privileged_entry.json_body['uuid'], dict({ - 'event_uuid': current_event.json_body['uuid'], + 'event_uuid': current_event_uuid, 'title': "Modified the title a regular user", 'description': "Modified the description", 'location': "Modified the location", diff --git a/phoenixRest/tests/views/application/__init__.py b/phoenixRest/tests/views/application/__init__.py index 3aa9689..7e87390 100644 --- a/phoenixRest/tests/views/application/__init__.py +++ b/phoenixRest/tests/views/application/__init__.py @@ -1,9 +1,16 @@ # Test creating applications, and that the user is qualified to do so from wsgiref.util import application_uri +from datetime import datetime, timedelta +from phoenixRest.models.core.event import Event +from phoenixRest.models.core.user import User +from phoenixRest.models.crew.application import Application, ApplicationState +from phoenixRest.models.crew.application_crew_mapping import ApplicationCrewMapping +from phoenixRest.models.crew.crew import Crew + from test_app import TestApp -def create_application(testapp:TestApp, token, application_crews: list): +def create_application(testapp:TestApp, token, application_crews: list, event): # Get the current user, ensure there is no avatar currentUser = testapp.get('/user/current', headers=dict({ "Authorization": "Bearer " + token @@ -19,7 +26,8 @@ def create_application(testapp:TestApp, token, application_crews: list): res = testapp.put_json('/application', dict({ 'crews': application_crews, - 'contents': 'I want to join please' + 'contents': 'I want to join please', + 'event_uuid': str(event.uuid) }), headers=dict({ "Authorization": "Bearer " + token }), status=200) @@ -36,7 +44,7 @@ def test_create_accept_appliations(testapp, upcoming_event): application_crew_candidates = list(filter(lambda crew: crew["is_applyable"], testapp.get('/crew', status=200).json_body)) - application_uuid = create_application(testapp, applicant_token, [application_crew_candidates[0]['uuid'], application_crew_candidates[1]['uuid']]) + application_uuid = create_application(testapp, applicant_token, [application_crew_candidates[0]['uuid'], application_crew_candidates[1]['uuid']], upcoming_event) # Try to accept the application res = testapp.patch_json('/application/%s' % application_uuid, dict({ @@ -61,9 +69,7 @@ def test_create_accept_appliations(testapp, upcoming_event): assert len(applicant_user['position_mappings']) > 0 - current_event = testapp.get('/event/current', status=200).json_body - - correct_mappings = list(filter(lambda mapping: mapping['event_uuid'] == current_event['uuid'], applicant_user['position_mappings'])) + correct_mappings = list(filter(lambda mapping: mapping['event_uuid'] == str(upcoming_event.uuid), applicant_user['position_mappings'])) assert len(correct_mappings) == 1 assert correct_mappings[0]['position']['crew_uuid'] == application_crew_candidates[0]['uuid'] @@ -74,7 +80,7 @@ def test_create_reject_appliations(testapp, upcoming_event): application_crew = list(filter(lambda crew: crew["is_applyable"], testapp.get('/crew', status=200).json_body))[0]['uuid'] - application_uuid = create_application(testapp, applicant_token, [application_crew]) + application_uuid = create_application(testapp, applicant_token, [application_crew], upcoming_event) # Try to accept the application res = testapp.patch_json('/application/%s' % application_uuid, dict({ @@ -98,9 +104,7 @@ def test_create_reject_appliations(testapp, upcoming_event): assert len(applicant_user['position_mappings']) > 0 - current_event = testapp.get('/event/current', status=200).json_body - - correct_mappings = list(filter(lambda mapping: mapping['event_uuid'] == current_event['uuid'], applicant_user['position_mappings'])) + correct_mappings = list(filter(lambda mapping: mapping['event_uuid'] == str(upcoming_event.uuid), applicant_user['position_mappings'])) assert len(correct_mappings) == 0 def hide_application(testapp, token, application_crews: list, upcoming_event): @@ -117,7 +121,7 @@ def hide_application(testapp, token, application_crews: list, upcoming_event): assert len(my_application_list) == 0 - application_uuid = create_application(testapp, applicant_token, [application_crew_candidates[0]['uuid'], application_crew_candidates[1]['uuid']]) + application_uuid = create_application(testapp, applicant_token, [application_crew_candidates[0]['uuid'], application_crew_candidates[1]['uuid']], upcoming_event) my_application_list = testapp.get('/application/my', headers=dict({ "Authorization": "Bearer " + applicant_token @@ -147,3 +151,37 @@ def hide_application(testapp, token, application_crews: list, upcoming_event): }), status=200).json_body assert len(my_application_list) == 0 + +def test_cannot_create_application_noncurrent_event(testapp, db, event_brand): + # Log in as applicant + applicant_token, refresh = testapp.auth_get_tokens('greg@example.com', 'sixcharacters') + + # Get the current user and upload an avatar (required for applications) + currentUser = testapp.get('/user/current', headers=dict({ + "Authorization": "Bearer " + applicant_token + }), status=200).json_body + + testapp.post('/user/%s/avatar' % currentUser['uuid'], params="x=%d&y=%d&w=%d&h=%d"% (0, 0, 600, 450), upload_files=[('file', "phoenixRest/tests/assets/avatar_test.png")], headers=dict({ + "Authorization": "Bearer " + applicant_token + }), status = 200) + + # This event has expired + event_start = datetime.now() - timedelta(days=65) + event_end = datetime.now() - timedelta(days=62) + past_event = Event("Past Event", event_start, event_end, 400, event_brand) + db.add(past_event) + db.flush() + + # Get applyable crews + application_crew_candidates = list(filter(lambda crew: crew["is_applyable"], testapp.get('/crew', status=200).json_body)) + + # Attempt to create an application for the past event - should fail + res = testapp.put_json('/application', dict({ + 'crews': [application_crew_candidates[0]['uuid']], + 'contents': 'I want to join please', + 'event_uuid': str(past_event.uuid) + }), headers=dict({ + "Authorization": "Bearer " + applicant_token + }), status=400) + + assert res.json_body['error'] == "Event is not current - you can't create an application for a non-current event" diff --git a/phoenixRest/tests/views/card_order/__init__.py b/phoenixRest/tests/views/card_order/__init__.py index 6fc0885..8e9cd63 100644 --- a/phoenixRest/tests/views/card_order/__init__.py +++ b/phoenixRest/tests/views/card_order/__init__.py @@ -14,20 +14,22 @@ def test_card_order(testapp:TestApp, upcoming_event): #? ---- __init__.py ---- # Admin orders a card for themself res = testapp.post_json("/card_order/", dict({ - "user_uuid" : admin_uuid + "user_uuid" : admin_uuid, + "event_uuid": str(upcoming_event.uuid) }), headers=dict({ 'Authorization': "Bearer " + admin_user_token }), status=200) # Admin orders a card with the wrong user uuid and it fails res = testapp.post_json("/card_order/", dict({ - "user_uuid" : str(uuid.uuid4()) + "user_uuid" : str(uuid.uuid4()), + "event_uuid": str(upcoming_event.uuid) }), headers=dict({ 'Authorization': "Bearer " + admin_user_token }), status=400) # Admin views all card orders, i.e the one they created - res = testapp.get("/card_order/", headers=dict({ + res = testapp.get(f"/event/{upcoming_event.uuid}/card_orders", headers=dict({ 'Authorization': "Bearer " + admin_user_token }), status=200).json_body assert len(res) == 1 @@ -61,7 +63,8 @@ def test_card_order(testapp:TestApp, upcoming_event): # Admin orders a new card for themself res = testapp.post_json("/card_order/", dict({ - "user_uuid" : admin_uuid + "user_uuid" : admin_uuid, + "event_uuid": str(upcoming_event.uuid) }), headers=dict({ 'Authorization': "Bearer " + admin_user_token }), status=200).json_body @@ -80,15 +83,8 @@ def test_card_order(testapp:TestApp, upcoming_event): }), status=400) # Admin views all card orders for the current event - res = testapp.get("/card_order/", headers=dict({ - 'Authorization': "Bearer " + admin_user_token - }), status=200) - assert len(res.json_body) == 2 - - # Admin views all card orders for a specified event, which happens to be the current one for convenience - current_event_uuid = testapp.get("/event/current/", status=200).json_body["uuid"] - - res = testapp.get(f"/card_order/", params=f"event_uuid={current_event_uuid}", headers=dict({ + res = testapp.get(f"/event/{upcoming_event.uuid}/card_orders", headers=dict({ 'Authorization': "Bearer " + admin_user_token }), status=200) assert len(res.json_body) == 2 + \ No newline at end of file diff --git a/phoenixRest/tests/views/consent_withdrawal_code/test_consent_withdrawal.py b/phoenixRest/tests/views/consent_withdrawal_code/test_consent_withdrawal.py index 386cd37..321382f 100644 --- a/phoenixRest/tests/views/consent_withdrawal_code/test_consent_withdrawal.py +++ b/phoenixRest/tests/views/consent_withdrawal_code/test_consent_withdrawal.py @@ -3,8 +3,6 @@ from phoenixRest.models.core.consent_withdrawal_code import ConsentWithdrawalCode def test_consent_withdrawal(testapp, db, upcoming_event): - testapp.ensure_typical_event() - test_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') target_token, refresh = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -26,7 +24,8 @@ def test_consent_withdrawal(testapp, db, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + test_token }), status=200).json_body diff --git a/phoenixRest/tests/views/email/test_consent_mail.py b/phoenixRest/tests/views/email/test_consent_mail.py index 2d5b7ee..fa454d9 100644 --- a/phoenixRest/tests/views/email/test_consent_mail.py +++ b/phoenixRest/tests/views/email/test_consent_mail.py @@ -7,8 +7,6 @@ from datetime import datetime def test_consent_mail_dryrun(db, testapp, upcoming_event): - testapp.ensure_typical_event() - # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') adam_token, refresh = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -20,7 +18,8 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): crew_mail_test_pre = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -28,7 +27,8 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): participant_mail_count_pre = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "participant_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -39,7 +39,8 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -56,7 +57,8 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -67,7 +69,8 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): crew_mail_test_post = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -75,7 +78,8 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): participant_mail_count_post = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "participant_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -93,7 +97,8 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -105,9 +110,7 @@ def test_consent_mail_dryrun(db, testapp, upcoming_event): assert len(consent_withdrawal_codes_post) == len(consent_withdrawal_codes_pre) + 1 -def test_consent_mail_no_participants(db, testapp, upcoming_event): - testapp.ensure_typical_event() - +def test_consent_mail_no_participants(db, testapp, upcoming_event, ticket_types): # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') adam_token, refresh = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -118,7 +121,8 @@ def test_consent_mail_no_participants(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -135,7 +139,8 @@ def test_consent_mail_no_participants(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -143,8 +148,7 @@ def test_consent_mail_no_participants(db, testapp, upcoming_event): assert consenting_user_result['count'] == 2 # Get existing ticket types - current_event = testapp.get('/event/current', status=200) - res = testapp.get('/event/%s/ticketType' % current_event.json_body['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) ticket_type = res.json_body[0] @@ -152,7 +156,8 @@ def test_consent_mail_no_participants(db, testapp, upcoming_event): # Give test a free ticket. Only works because test is an admin res = testapp.post_json('/ticket', dict({ 'ticket_type': ticket_type['uuid'], - 'recipient': adam_user['uuid'] + 'recipient': adam_user['uuid'], + 'event_uuid': str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) @@ -161,7 +166,8 @@ def test_consent_mail_no_participants(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -170,8 +176,6 @@ def test_consent_mail_no_participants(db, testapp, upcoming_event): def test_consent_mail_no_crew(db, testapp, upcoming_event): - testapp.ensure_typical_event() - # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') adam_token, refresh = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -182,7 +186,8 @@ def test_consent_mail_no_crew(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -199,7 +204,8 @@ def test_consent_mail_no_crew(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -214,7 +220,8 @@ def test_consent_mail_no_crew(db, testapp, upcoming_event): created_mapping = testapp.post_json('/position_mapping', { "position_uuid": position_candidates[0]['uuid'], - "user_uuid": adam_user['uuid'] + "user_uuid": adam_user['uuid'], + 'event_uuid': str(upcoming_event.uuid) }, headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -225,7 +232,8 @@ def test_consent_mail_no_crew(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -234,8 +242,6 @@ def test_consent_mail_no_crew(db, testapp, upcoming_event): def test_consent_mail_no_applicants(db, testapp, upcoming_event): - testapp.ensure_typical_event() - # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') adam_token, refresh = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -246,7 +252,8 @@ def test_consent_mail_no_applicants(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -263,14 +270,14 @@ def test_consent_mail_no_applicants(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body assert consenting_user_result['count'] == 2 - current_event = testapp.get('/event/current', status=200).json_body testapp.post('/user/%s/avatar' % adam_user['uuid'], params="x=%d&y=%d&w=%d&h=%d"% (0, 0, 600, 450), upload_files=[('file', "phoenixRest/tests/assets/avatar_test.png")], headers=dict({ "Authorization": "Bearer " + adam_token }), status = 200) @@ -279,7 +286,8 @@ def test_consent_mail_no_applicants(db, testapp, upcoming_event): res = testapp.put_json('/application', dict({ 'crews': [application_crews[0]['uuid']], - 'contents': 'I want to join please' + 'contents': 'I want to join please', + 'event_uuid': str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + adam_token }), status=200) @@ -288,7 +296,8 @@ def test_consent_mail_no_applicants(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body diff --git a/phoenixRest/tests/views/email/test_email_dryrun.py b/phoenixRest/tests/views/email/test_email_dryrun.py index 0503f80..b625da5 100644 --- a/phoenixRest/tests/views/email/test_email_dryrun.py +++ b/phoenixRest/tests/views/email/test_email_dryrun.py @@ -9,7 +9,6 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): Participants should not receive these mails. Assumes nobody has consented to marketing mail""" - testapp.ensure_typical_event() # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') @@ -22,7 +21,8 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): participant_mail_count_pre = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "participant_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -30,7 +30,8 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): consenting_user_pre = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -39,7 +40,8 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): crew_mail_test = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -50,7 +52,8 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): position = db.query(Position).filter(Position.crew_uuid == None).first() mapping = testapp.post_json('/position_mapping', dict({ "user_uuid": adam_user['uuid'], - "position_uuid": str(position.uuid) + "position_uuid": str(position.uuid), + "event_uuid": str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) @@ -58,7 +61,8 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): crew_mail_test = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -72,7 +76,8 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): # Create a position mapping mapping = testapp.post_json('/position_mapping', dict({ "user_uuid": adam_user['uuid'], - "position_uuid": str(position.uuid) + "position_uuid": str(position.uuid), + "event_uuid": str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) @@ -80,7 +85,8 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): crew_mail_test = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -91,14 +97,16 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): participant_mail_count_post = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "participant_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] consenting_user_post = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -107,12 +115,11 @@ def test_crew_mail_dryryn(db, testapp, upcoming_event): assert consenting_user_pre == consenting_user_post -def test_participant_mail_dryrun(testapp, upcoming_event): +def test_participant_mail_dryrun(testapp, upcoming_event, ticket_types): """Tests that all participants get e-mails when the participant_info category is used. Crew members should not receive these mails if they don't have a ticket Assumes nobody has consented to marketing mail""" - testapp.ensure_typical_event() # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') @@ -124,7 +131,8 @@ def test_participant_mail_dryrun(testapp, upcoming_event): participant_mail_test_results = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "participant_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -135,30 +143,31 @@ def test_participant_mail_dryrun(testapp, upcoming_event): crew_mail_test_pre = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] consenting_user_pre = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] - # Now give adam a free ticket - current_event = testapp.get('/event/current', status=200) # Get existing ticket types - res = testapp.get('/event/%s/ticketType' % current_event.json_body['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) ticket_type = res.json_body[0] res = testapp.post_json('/ticket', dict({ 'ticket_type': ticket_type['uuid'], - 'recipient': adam_user['uuid'] + 'recipient': adam_user['uuid'], + 'event_uuid': str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) @@ -167,7 +176,8 @@ def test_participant_mail_dryrun(testapp, upcoming_event): participant_mail_test_results = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "participant_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -178,14 +188,16 @@ def test_participant_mail_dryrun(testapp, upcoming_event): crew_mail_test_post = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] consenting_user_post = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body['count'] @@ -194,22 +206,19 @@ def test_participant_mail_dryrun(testapp, upcoming_event): assert consenting_user_pre == consenting_user_post def test_invalid_mail_category_dryrun(testapp, upcoming_event): - testapp.ensure_typical_event() - # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') target_users = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "swag", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=400) def test_consent_mail_age_limit(db, testapp, upcoming_event): - testapp.ensure_typical_event() - # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') adam_token, refresh = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -218,9 +227,7 @@ def test_consent_mail_age_limit(db, testapp, upcoming_event): adam_user = testapp.get_user(adam_token) # Get current event, set an age limit - current_event = testapp.get_current_event(db) - current_event.participant_age_limit_inclusive = 300 - db.add(current_event) + upcoming_event.participant_age_limit_inclusive = 300 db.flush() # Add record reflecting that adam consented to marketing mail @@ -233,7 +240,8 @@ def test_consent_mail_age_limit(db, testapp, upcoming_event): consenting_user_result = testapp.post_json('/email/dryrun', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body diff --git a/phoenixRest/tests/views/email/test_email_send.py b/phoenixRest/tests/views/email/test_email_send.py index 8cd345d..9d37393 100644 --- a/phoenixRest/tests/views/email/test_email_send.py +++ b/phoenixRest/tests/views/email/test_email_send.py @@ -3,14 +3,13 @@ from phoenixRest.models.core.user_consent import UserConsent, ConsentType def test_crew_mail_sending(testapp, upcoming_event): - testapp.ensure_typical_event() - sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') crew_mail_test = testapp.post_json('/email/send', dict({ 'recipient_category': "crew_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -18,14 +17,13 @@ def test_crew_mail_sending(testapp, upcoming_event): assert crew_mail_test['sent'] == 3 # The user is a crew member already def test_participant_mail_sending(testapp, upcoming_event): - testapp.ensure_typical_event() - sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') crew_mail_test = testapp.post_json('/email/send', dict({ 'recipient_category': "participant_info", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -33,8 +31,6 @@ def test_participant_mail_sending(testapp, upcoming_event): assert crew_mail_test['sent'] == 1 def test_consent_mail_sending(db, testapp, upcoming_event): - testapp.ensure_typical_event() - sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') adam_token, refresh = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -49,7 +45,8 @@ def test_consent_mail_sending(db, testapp, upcoming_event): consent_mail_test = testapp.post_json('/email/send', dict({ 'recipient_category': "event_notification", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -57,15 +54,14 @@ def test_consent_mail_sending(db, testapp, upcoming_event): assert consent_mail_test['sent'] == 2 def test_invalid_mail_category_send(testapp, upcoming_event): - testapp.ensure_typical_event() - # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') target_users = testapp.post_json('/email/send', dict({ 'recipient_category': "swag", 'subject': "hello", - 'body': "# Foo bar\nHello" + 'body': "# Foo bar\nHello", + 'brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=400) diff --git a/phoenixRest/tests/views/event/__init__.py b/phoenixRest/tests/views/event/__init__.py index 1ae453b..bc1ace4 100644 --- a/phoenixRest/tests/views/event/__init__.py +++ b/phoenixRest/tests/views/event/__init__.py @@ -1,25 +1,9 @@ -# Test getting the current event -def test_get_event(testapp, upcoming_event): - res = testapp.get('/event/current', status=200) - assert res.json_body['uuid'] is not None - - token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') - - res = testapp.get('/event/%s' % res.json_body['uuid'], headers=dict({ - "Authorization": "Bearer " + token - }), status=200) - assert res is not None - - # Get ticket types for an event(we will use the current one) def test_get_ticket_types(testapp, upcoming_event): - current_event = testapp.get('/event/current', status=200) - assert current_event.json_body['uuid'] is not None - token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') # Ensure there are ticket types. By default there aren't - res = testapp.get('/event/%s/ticketType' % current_event.json_body['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + token }), status=200) @@ -31,14 +15,14 @@ def test_get_ticket_types(testapp, upcoming_event): }), status=200).json_body # Add a ticket type - testapp.put_json('/event/%s/ticketType' % current_event.json_body['uuid'], dict({ + testapp.put_json('/event/%s/ticketType' % upcoming_event.uuid, dict({ 'ticket_type_uuid': ticket_types[0]['uuid'] }), headers=dict({ "Authorization": "Bearer " + token }), status=200) # The ticket type should now be added - res = testapp.get('/event/%s/ticketType' % current_event.json_body['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + token }), status=200) diff --git a/phoenixRest/tests/views/event_brand/__init__.py b/phoenixRest/tests/views/event_brand/__init__.py new file mode 100644 index 0000000..552939c --- /dev/null +++ b/phoenixRest/tests/views/event_brand/__init__.py @@ -0,0 +1,113 @@ +from phoenixRest.models.core.event_brand import EventBrand +from phoenixRest.models.core.event import Event +from datetime import datetime, timedelta + + +def test_event_brand_create_and_list(testapp, db): + """Test creating and listing event brands""" + # Admin token is required for creating brands + token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') + + # List event brands - should be empty initially + res = testapp.get('/event_brand', status=200) + initial_count = len(res.json_body) + + # Create a new event brand + res = testapp.post_json('/event_brand', { + 'name': 'Test Brand' + }, headers={ + "Authorization": "Bearer " + token + }, status=200) + + # Verify the response + assert res.json_body['name'] == 'Test Brand' + assert 'uuid' in res.json_body + brand_uuid = res.json_body['uuid'] + + # List event brands again - should now have one more brand + res = testapp.get('/event_brand', status=200) + assert len(res.json_body) == initial_count + 1 + assert any(b['name'] == 'Test Brand' for b in res.json_body) + + # Fetch the created brand using the instance endpoint + res = testapp.get('/event_brand/%s' % brand_uuid, status=200) + assert res.json_body['uuid'] == brand_uuid + assert res.json_body['name'] == 'Test Brand' + + +def test_get_current_event_for_brand(testapp, db): + """Test getting the current event for a specific brand""" + # Create a brand + brand = EventBrand("Test Brand") + db.add(brand) + db.flush() + + # Create an upcoming event for this brand + event_start = datetime.now() + timedelta(days=10) + event_end = datetime.now() + timedelta(days=13) + event = Event("Test Event for Brand", event_start, event_end, 400, brand) + db.add(event) + db.flush() + + # Get the current event for this brand + res = testapp.get('/event_brand/%s/current_event' % str(brand.uuid), status=200) + assert res.json_body is not None + assert res.json_body['name'] == 'Test Event for Brand' + assert res.json_body['uuid'] == str(event.uuid) + assert res.json_body['event_brand_uuid'] == str(brand.uuid) + + +def test_get_current_event_no_events(testapp, db): + """Test getting current event when brand has no events""" + # Create a brand with no events + brand = EventBrand("Empty Brand") + db.add(brand) + db.flush() + + # Should return null when there are no events + res = testapp.get('/event_brand/%s/current_event' % str(brand.uuid), status=200) + assert res.json_body is None + + +def test_get_current_event_only_past_events(testapp, db): + """Test getting current event when brand only has past events""" + # Create a brand + brand = EventBrand("Past Events Brand") + db.add(brand) + db.flush() + + # Create a past event + event_start = datetime.now() - timedelta(days=30) + event_end = datetime.now() - timedelta(days=27) + event = Event("Past Event", event_start, event_end, 400, brand) + db.add(event) + db.flush() + + # Should return null when there are only past events + res = testapp.get('/event_brand/%s/current_event' % str(brand.uuid), status=200) + assert res.json_body is None + + +def test_get_current_event_multiple_upcoming(testapp, db): + """Test getting current event when brand has multiple upcoming events - should return earliest""" + # Create a brand + brand = EventBrand("Multiple Events Brand") + db.add(brand) + + # Create multiple upcoming events + event1_start = datetime.now() + timedelta(days=20) + event1_end = datetime.now() + timedelta(days=23) + event1 = Event("Later Event", event1_start, event1_end, 400, brand) + db.add(event1) + + event2_start = datetime.now() + timedelta(days=10) + event2_end = datetime.now() + timedelta(days=13) + event2 = Event("Earlier Event", event2_start, event2_end, 400, brand) + db.add(event2) + db.flush() + + # Should return the earliest upcoming event + res = testapp.get('/event_brand/%s/current_event' % str(brand.uuid), status=200) + assert res.json_body is not None + assert res.json_body['name'] == 'Earlier Event' + assert res.json_body['uuid'] == str(event2.uuid) diff --git a/phoenixRest/tests/views/payment/__init__.py b/phoenixRest/tests/views/payment/__init__.py index 5757884..e428a6e 100644 --- a/phoenixRest/tests/views/payment/__init__.py +++ b/phoenixRest/tests/views/payment/__init__.py @@ -6,11 +6,8 @@ from datetime import datetime, timedelta -def _create_store_session(testapp, token): - res = testapp.get('/event/current', status=200) - assert res.json_body['uuid'] is not None - - res = testapp.get('/event/%s/ticketType' % res.json_body['uuid'], headers=dict({ +def _create_store_session(testapp, event, token): + res = testapp.get('/event/%s/ticketType' % event.uuid, headers=dict({ "Authorization": "Bearer " + token }), status=200) @@ -18,7 +15,8 @@ def _create_store_session(testapp, token): res = testapp.put_json('/store_session', dict({ 'cart': [ {'qty': 1, 'uuid': res.json_body[0]['uuid']} - ] + ], + 'event_uuid': str(event.uuid) }), headers=dict({ "Authorization": "Bearer " + token }), status=200) @@ -28,20 +26,16 @@ def _create_store_session(testapp, token): assert store_session is not None return store_session -def test_ticket_sale_start_limit(testapp, db, upcoming_event): - testapp.ensure_typical_event() +def test_ticket_sale_start_limit(testapp, db, upcoming_event, ticket_types): # Jeff doesn't have permission to buy tickets any time token, refresh = testapp.auth_get_tokens('jeff@example.com', 'sixcharacters') - current_event = testapp.get('/event/current', status=200).json_body - assert current_event['uuid'] is not None - # Make sure buying tickets is illegal - event_instance = db.query(Event).filter(Event.uuid == current_event['uuid']).first() + event_instance = db.query(Event).filter(Event.uuid == upcoming_event.uuid).first() event_instance.booking_time = datetime.now() + timedelta(days=1) transaction.commit() - res = testapp.get('/event/%s/ticketType' % current_event['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + token }), status=200) @@ -55,11 +49,10 @@ def test_ticket_sale_start_limit(testapp, db, upcoming_event): }), status=400) # Test if we can create a payment -def test_payment_flow_vipps(testapp, upcoming_event): - testapp.ensure_typical_event() +def test_payment_flow_vipps(testapp, upcoming_event, ticket_types): token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') - store_session = _create_store_session(testapp, token) + store_session = _create_store_session(testapp, upcoming_event, token) # Create a payment res = testapp.post_json('/payment', dict({ 'store_session': store_session, @@ -137,11 +130,10 @@ def test_payment_flow_vipps(testapp, upcoming_event): # Test if we can create a payment -def test_payment_flow_stripe(testapp, upcoming_event): - testapp.ensure_typical_event() +def test_payment_flow_stripe(testapp, upcoming_event, ticket_types): token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') - store_session = _create_store_session(testapp, token) + store_session = _create_store_session(testapp, upcoming_event, token) # Create a payment res = testapp.post_json('/payment', dict({ 'store_session': store_session, diff --git a/phoenixRest/tests/views/position_mapping/__init__.py b/phoenixRest/tests/views/position_mapping/__init__.py index e69d364..6f2a9c3 100644 --- a/phoenixRest/tests/views/position_mapping/__init__.py +++ b/phoenixRest/tests/views/position_mapping/__init__.py @@ -3,7 +3,7 @@ from phoenixRest.models.crew.position_mapping import PositionMapping -def test_create_delete_position_mapping(testapp): +def test_create_delete_position_mapping(testapp, upcoming_event): # Log in as the test user token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') test_user_token, _ = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -16,7 +16,8 @@ def test_create_delete_position_mapping(testapp): created_mapping = testapp.post_json('/position_mapping', { "position_uuid": position_candidates[0]['uuid'], - "user_uuid": test_user['uuid'] + "user_uuid": test_user['uuid'], + "event_uuid": str(upcoming_event.uuid) }, headers=dict({ "Authorization": "Bearer " + token }), status=200).json_body @@ -51,7 +52,7 @@ def test_create_delete_position_mapping(testapp): }), status=404) # Make sure a permissionless user can't make permission mappings. Low-hanging fruit to test -def test_no_permissionless_promotion(testapp): +def test_no_permissionless_promotion(testapp, upcoming_event): # Log in as the test user token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') test_user_token, _ = testapp.auth_get_tokens('adam@example.com', 'sixcharacters') @@ -64,7 +65,8 @@ def test_no_permissionless_promotion(testapp): testapp.post_json('/position_mapping', { "position_uuid": position_candidates[0]['uuid'], - "user_uuid": test_user['uuid'] + "user_uuid": test_user['uuid'], + "event_uuid": str(upcoming_event.uuid) }, headers=dict({ "Authorization": "Bearer " + test_user_token }), status=403) diff --git a/phoenixRest/tests/views/statistics/test_ticket_sales.py b/phoenixRest/tests/views/statistics/test_ticket_sales.py index cdd7608..08ea338 100644 --- a/phoenixRest/tests/views/statistics/test_ticket_sales.py +++ b/phoenixRest/tests/views/statistics/test_ticket_sales.py @@ -1,14 +1,12 @@ from phoenixRest.models.core.event import Event -def test_smoketest_ticket_sales(db, testapp, ticketsale_ongoing_event): +def test_smoketest_ticket_sales(db, testapp, ticketsale_ongoing_event, ongoing_ticket_types): """Simple test that just makes sure ticket sales statistics isn't obviously broken """ # test is an admin token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') unprivileged_token, refresh = testapp.auth_get_tokens('jeff@example.com', 'sixcharacters') - testapp.ensure_typical_event() - stats = testapp.get('/statistics/ticket_sales', headers=dict({ "Authorization": "Bearer " + token }), status=200).json_body @@ -18,13 +16,13 @@ def test_smoketest_ticket_sales(db, testapp, ticketsale_ongoing_event): assert len(stats) == len(all_events) # No tickets should ever be sold - current_stats = next(filter(lambda stat: stat["event"]["uuid"] == str(testapp.get_current_event(db).uuid), stats)) + current_stats = next(filter(lambda stat: stat["event"]["uuid"] == str(ticketsale_ongoing_event.uuid), stats)) assert sum(map(lambda day: day["count"], current_stats["days"])) == 0 # Get ticket types for the next stage ticket_types = filter( lambda type: type["price"] > 0, - testapp.get(f'/event/{testapp.get_current_event(db).uuid}/ticketType', status=200).json_body + testapp.get(f'/event/{ticketsale_ongoing_event.uuid}/ticketType', status=200).json_body ) # Get unprivileged user UUID @@ -38,18 +36,18 @@ def test_smoketest_ticket_sales(db, testapp, ticketsale_ongoing_event): for i in range(0, 2): res = testapp.post_json('/ticket', dict({ 'ticket_type': next(ticket_types)['uuid'], - 'recipient': unprivileged_user['uuid'] + 'recipient': unprivileged_user['uuid'], + 'event_uuid': str(ticketsale_ongoing_event.uuid) }), headers=dict({ "Authorization": "Bearer " + token }), status=200) - print(f"Ticket: {res.json_body}") # Ensure at least one ticket is sold stats = testapp.get('/statistics/ticket_sales', headers=dict({ "Authorization": "Bearer " + token }), status=200).json_body - current_event_uuid = str(testapp.get_current_event(db).uuid) + current_event_uuid = str(ticketsale_ongoing_event.uuid) print(f"Current event UUID: {current_event_uuid}") current_stats = next(filter(lambda stat: stat["event"]["uuid"] == current_event_uuid, stats)) diff --git a/phoenixRest/tests/views/store_session/__init__.py b/phoenixRest/tests/views/store_session/__init__.py index 85ea5a4..59136d9 100644 --- a/phoenixRest/tests/views/store_session/__init__.py +++ b/phoenixRest/tests/views/store_session/__init__.py @@ -1,12 +1,8 @@ # Test if we can reserve a store session -def test_create_store_session(testapp, upcoming_event): - testapp.ensure_typical_event() - res = testapp.get('/event/current', status=200) - assert res.json_body['uuid'] is not None - +def test_create_store_session(testapp, upcoming_event, ticket_types): token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') - res = testapp.get('/event/%s/ticketType' % res.json_body['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + token }), status=200) @@ -14,7 +10,8 @@ def test_create_store_session(testapp, upcoming_event): res = testapp.put_json('/store_session', dict({ 'cart': [ {'qty': 1, 'uuid': res.json_body[0]['uuid']} - ] + ], + 'event_uuid': str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + token }), status=200) diff --git a/phoenixRest/tests/views/ticket/__init__.py b/phoenixRest/tests/views/ticket/__init__.py index bf15b05..7d09e4a 100644 --- a/phoenixRest/tests/views/ticket/__init__.py +++ b/phoenixRest/tests/views/ticket/__init__.py @@ -1,6 +1,5 @@ -def test_ticket_creation_permissions(testapp, upcoming_event): - testapp.ensure_typical_event() +def test_ticket_creation_permissions(testapp, upcoming_event, ticket_types): # test is an admin privileged_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') unprivileged_token, refresh = testapp.auth_get_tokens('jeff@example.com', 'sixcharacters') @@ -9,12 +8,8 @@ def test_ticket_creation_permissions(testapp, upcoming_event): privileged_user = testapp.get_user(privileged_token) unprivileged_user = testapp.get_user(unprivileged_token) - # Current event - current_event = testapp.get('/event/current', status=200) - assert current_event.json_body['uuid'] is not None - # Get existing ticket types - res = testapp.get('/event/%s/ticketType' % current_event.json_body['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + privileged_token }), status=200) ticket_type = res.json_body[0] @@ -22,7 +17,8 @@ def test_ticket_creation_permissions(testapp, upcoming_event): # Give test a free ticket. Only works because test is an admin res = testapp.post_json('/ticket', dict({ 'ticket_type': ticket_type['uuid'], - 'recipient': unprivileged_user['uuid'] + 'recipient': unprivileged_user['uuid'], + 'event_uuid': str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + privileged_token }), status=200) @@ -30,7 +26,8 @@ def test_ticket_creation_permissions(testapp, upcoming_event): # Now try with a non-authorized user. Should error. res = testapp.post_json('/ticket', dict({ 'ticket_type': ticket_type['uuid'], - 'recipient': unprivileged_user['uuid'] + 'recipient': unprivileged_user['uuid'], + 'event_uuid': str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + unprivileged_token }), status=403) \ No newline at end of file diff --git a/phoenixRest/tests/views/ticket/test_seating.py b/phoenixRest/tests/views/ticket/test_seating.py index 13e6d5b..2379477 100644 --- a/phoenixRest/tests/views/ticket/test_seating.py +++ b/phoenixRest/tests/views/ticket/test_seating.py @@ -1,4 +1,5 @@ from phoenixRest.models.core.event import Event +from phoenixRest.models.tickets.seatmap import Seatmap import json import transaction @@ -34,11 +35,20 @@ def ensure_seatmap(testapp, token, event_uuid): "Authorization": "Bearer " + token }), status=200).json_body + # Re-fetch the seatmap seatmap = testapp.get('/seatmap/%s' % seatmap['uuid'], headers=dict({ "Authorization": "Bearer " + token }), status=200).json_body + # Set the seatmap as current for the event + testapp.patch_json(f'/event/{event_uuid}', dict({ + 'seatmap_uuid': seatmap['uuid'] + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=200).json_body + + return seatmap def ensure_ticket(testapp, token, event_uuid): @@ -53,31 +63,48 @@ def ensure_ticket(testapp, token, event_uuid): # Give test a free ticket. Only works because test is an admin res = testapp.post_json('/ticket', dict({ 'ticket_type': ticket_type['uuid'], - 'recipient': current_user['uuid'] + 'recipient': current_user['uuid'], + "event_uuid": str(event_uuid) }), headers=dict({ "Authorization": "Bearer " + token }), status=200) # List the tickets owned by the test user owned_tickets = testapp.get('/user/%s/owned_tickets' % current_user['uuid'], headers=dict({ - "Authorization": "Bearer " + token + "Authorization": "Bearer " + token }), status=200).json_body assert len(owned_tickets) > 0 return owned_tickets[0] -def test_ticket_seating(testapp, db, upcoming_event): - testapp.ensure_typical_event() +def test_seatmap_wrong_event(testapp, db, upcoming_event, ticket_types): + """Test that accessing seatmap availability with wrong event returns 400""" + # test is an admin + token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') + + # Create a seatmap that is NOT associated with the event + seatmap = testapp.put_json('/seatmap', dict({ + 'name': 'Test seatmap', + 'description': 'seatmap' + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=200).json_body + + # Try to get availability for an event that doesn't use this seatmap + # This should return 400 because the event doesn't use this seatmap + testapp.get('/seatmap/%s/availability?event_uuid=%s' % (seatmap['uuid'], upcoming_event.uuid), headers=dict({ + "Authorization": "Bearer " + token + }), status=400) + +def test_ticket_seating(testapp, db, upcoming_event, ticket_types): # test is an admin token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') # Current event - current_event = testapp.get('/event/current', status=200).json_body - assert current_event['uuid'] is not None - seatmap = ensure_seatmap(testapp, token, current_event['uuid']) + seatmap = ensure_seatmap(testapp, token, upcoming_event.uuid) - seat_ticket = ensure_ticket(testapp, token, current_event['uuid']) + seat_ticket = ensure_ticket(testapp, token, upcoming_event.uuid) - availability = testapp.get('/seatmap/%s/availability' % seatmap['uuid'], headers=dict({ + availability = testapp.get('/seatmap/%s/availability?event_uuid=%s' % (seatmap['uuid'], upcoming_event.uuid), headers=dict({ "Authorization": "Bearer " + token }), status=200).json_body @@ -95,7 +122,7 @@ def test_ticket_seating(testapp, db, upcoming_event): assert seat is not None # Make sure seating a ticket is illegal - event_instance = db.query(Event).filter(Event.uuid == current_event['uuid']).first() + event_instance = db.query(Event).filter(Event.uuid == upcoming_event.uuid).first() event_instance.booking_time = datetime.now() event_instance.seating_time_delta = 60*60 transaction.commit() @@ -108,7 +135,7 @@ def test_ticket_seating(testapp, db, upcoming_event): }), status=400) # Make sure sating is legal - event_instance = db.query(Event).filter(Event.uuid == current_event['uuid']).first() + event_instance = db.query(Event).filter(Event.uuid == upcoming_event.uuid).first() event_instance.booking_time = datetime.now() - timedelta(days=1) event_instance.seating_time_delta = 30 transaction.commit() @@ -121,7 +148,7 @@ def test_ticket_seating(testapp, db, upcoming_event): }), status=200) # Ensure the seatmap updated - availability = testapp.get('/seatmap/%s/availability' % seatmap['uuid'], headers=dict({ + availability = testapp.get('/seatmap/%s/availability?event_uuid=%s' % (seatmap['uuid'], upcoming_event.uuid), headers=dict({ "Authorization": "Bearer " + token }), status=200).json_body @@ -138,7 +165,132 @@ def test_ticket_seating(testapp, db, upcoming_event): # Ensure the ticket now has a seat ticket = testapp.get('/ticket/%s' % seat_ticket['ticket_id'], headers=dict({ - "Authorization": "Bearer " + token + "Authorization": "Bearer " + token }), status=200).json_body assert ticket['seat'] is not None - assert ticket['seat']['uuid'] == seat['uuid'] \ No newline at end of file + assert ticket['seat']['uuid'] == seat['uuid'] + +def test_ticket_seating_non_current_event(testapp, db, event_brand, ticket_types): + """Test that seating a ticket for a non-current event is rejected""" + # test is an admin + token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') + + # Strategy: Create an even that is currently current. Set everything up. Then change its start and end time. + earlier_event = Event("Earlier event", datetime.now() + timedelta(days=20), datetime.now() + timedelta(days=22), 400, event_brand) + earlier_event.booking_time = datetime.now() + timedelta(days=2) + earlier_event.seating_time_delta = 30 + db.add(earlier_event) + db.flush() + + seatmap = ensure_seatmap(testapp, token, earlier_event.uuid) + + # Add ticket types to the earlier event + all_ticket_types = testapp.get('/ticketType', headers=dict({ + 'Authorization': "Bearer " + token + }), status=200).json_body + + for ticket_type in all_ticket_types: + testapp.put_json('/event/%s/ticketType' % earlier_event.uuid, dict({ + 'ticket_type_uuid': ticket_type['uuid'] + }), headers=dict({ + 'Authorization': "Bearer " + token + }), status=200) + + # Create a ticket for the earlier event + seat_ticket = ensure_ticket(testapp, token, earlier_event.uuid) + + # Get an available seat + availability = testapp.get('/seatmap/%s/availability?event_uuid=%s' % (seatmap['uuid'], earlier_event.uuid), headers=dict({ + "Authorization": "Bearer " + token + }), status=200).json_body + + seat = None + for row in availability['rows']: + for seat_candidate in row['seats']: + if seat_candidate['taken'] == False: + seat = seat_candidate + break + if seat: + break + + assert seat is not None + + # Now move the event to be in the past, and test seating + earlier_event.start_time = datetime.now() - timedelta(days=365) + earlier_event.end_time = datetime.now() - timedelta(days=368) + db.flush() + + # Try to seat the ticket - should fail because event is not current + response = testapp.put_json('/ticket/%s/seat' % seat_ticket['ticket_id'], dict({ + 'seat_uuid': seat['uuid'] + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=400).json_body + + assert 'error' in response + assert 'not current' in response['error'] + + # And create a new event in the future. Should still not work + new_event = Event("Earlier event", datetime.now() + timedelta(days=300), datetime.now() + timedelta(days=303), 400, event_brand) + new_event.booking_time = datetime.now() + timedelta(days=250) + new_event.seating_time_delta = 30 + db.add(new_event) + db.flush() + + response = testapp.put_json('/ticket/%s/seat' % seat_ticket['ticket_id'], dict({ + 'seat_uuid': seat['uuid'] + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=400).json_body + + assert 'error' in response + assert 'not current' in response['error'] + +def test_ticket_seating_wrong_seatmap(testapp, db, upcoming_event, ticket_types): + """Test that seating a ticket on a seat from a different seatmap is rejected""" + # test is an admin + token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') + + # Create seatmap for the event + seatmap = ensure_seatmap(testapp, token, upcoming_event.uuid) + seat_ticket = ensure_ticket(testapp, token, upcoming_event.uuid) + + # Create a DIFFERENT seatmap that is NOT associated with the event + other_seatmap = testapp.put_json('/seatmap', dict({ + 'name': 'Other seatmap', + 'description': 'different seatmap' + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=200).json_body + + # Add a row to the other seatmap + other_row = testapp.put_json('/seatmap/%s/row' % other_seatmap['uuid'], dict({ + 'row_number': 1, + 'x': 10, + 'y': 10, + 'horizontal': False + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=200).json_body + + # Add a seat to the other seatmap + other_seat = testapp.put_json('/row/%s/seat' % other_row['uuid'], dict({ + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=200).json_body + + # Make sure seating is allowed time-wise + event_instance = db.query(Event).filter(Event.uuid == upcoming_event.uuid).first() + event_instance.booking_time = datetime.now() - timedelta(days=1) + event_instance.seating_time_delta = 30 + transaction.commit() + + # Try to seat the ticket on a seat from the wrong seatmap - should fail + response = testapp.put_json('/ticket/%s/seat' % seat_ticket['ticket_id'], dict({ + 'seat_uuid': other_seat['uuid'] + }), headers=dict({ + "Authorization": "Bearer " + token + }), status=400).json_body + + assert 'error' in response + assert 'different seatmap' in response['error'] \ No newline at end of file diff --git a/phoenixRest/tests/views/ticket_transfer/__init__.py b/phoenixRest/tests/views/ticket_transfer/__init__.py index 4597ec5..de514cf 100644 --- a/phoenixRest/tests/views/ticket_transfer/__init__.py +++ b/phoenixRest/tests/views/ticket_transfer/__init__.py @@ -1,6 +1,5 @@ -def test_ticket_transfer_flow(testapp, upcoming_event): - testapp.ensure_typical_event() +def test_ticket_transfer_flow(testapp, upcoming_event, ticket_types): # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') receiver_token, refresh = testapp.auth_get_tokens('jeff@example.com', 'sixcharacters') @@ -11,12 +10,8 @@ def test_ticket_transfer_flow(testapp, upcoming_event): receiver_user = testapp.get_user(receiver_token) receiver_2_user = testapp.get_user(receiver_2_token) - # Current event - current_event = testapp.get('/event/current', status=200) - assert current_event.json_body['uuid'] is not None - # Get existing ticket types - res = testapp.get('/event/%s/ticketType' % current_event.json_body['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) ticket_type = res.json_body[0] @@ -24,7 +19,8 @@ def test_ticket_transfer_flow(testapp, upcoming_event): # Give test a free ticket. Only works because test is an admin res = testapp.post_json('/ticket', dict({ 'ticket_type': ticket_type['uuid'], - 'recipient': sender_user['uuid'] + 'recipient': sender_user['uuid'], + "event_uuid": str(upcoming_event.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) @@ -76,14 +72,14 @@ def test_ticket_transfer_flow(testapp, upcoming_event): assert len(owned_tickets_recipient_post_transfer) > len(owned_tickets_recipient) # Check that both people can see the ticket transfer, and assert it is not reversed and not expired - sender_transfers = testapp.get('/user/%s/ticket_transfers' % sender_user['uuid'], headers=dict({ - "Authorization": "Bearer " + sender_token + sender_transfers = testapp.get('/user/%s/ticket_transfers?event_uuid=%s' % (sender_user['uuid'], upcoming_event.uuid), headers=dict({ + "Authorization": "Bearer " + sender_token, }), status=200).json_body assert len(sender_transfers) == 1 - receiver_transfers = testapp.get('/user/%s/ticket_transfers' % receiver_user['uuid'], headers=dict({ - "Authorization": "Bearer " + receiver_token + receiver_transfers = testapp.get('/user/%s/ticket_transfers?event_uuid=%s' % (receiver_user['uuid'], upcoming_event.uuid), headers=dict({ + "Authorization": "Bearer " + receiver_token, }), status=200).json_body assert len(receiver_transfers) == 1 @@ -113,14 +109,14 @@ def test_ticket_transfer_flow(testapp, upcoming_event): }), status=200) # Check that the transfer isn't gone - sender_transfers = testapp.get('/user/%s/ticket_transfers' % sender_user['uuid'], headers=dict({ - "Authorization": "Bearer " + sender_token + sender_transfers = testapp.get('/user/%s/ticket_transfers?event_uuid=%s' % (sender_user['uuid'], upcoming_event.uuid), headers=dict({ + "Authorization": "Bearer " + sender_token, }), status=200).json_body assert len(sender_transfers) == 1 - receiver_transfers = testapp.get('/user/%s/ticket_transfers' % receiver_user['uuid'], headers=dict({ - "Authorization": "Bearer " + receiver_token + receiver_transfers = testapp.get('/user/%s/ticket_transfers?event_uuid=%s' % (receiver_user['uuid'], upcoming_event.uuid), headers=dict({ + "Authorization": "Bearer " + receiver_token, }), status=200).json_body assert len(receiver_transfers) == 1 diff --git a/phoenixRest/tests/views/ticket_voucher/__init__.py b/phoenixRest/tests/views/ticket_voucher/__init__.py index e347674..4ccf20e 100644 --- a/phoenixRest/tests/views/ticket_voucher/__init__.py +++ b/phoenixRest/tests/views/ticket_voucher/__init__.py @@ -2,9 +2,7 @@ from datetime import datetime -def test_ticket_voucher_flow(testapp, upcoming_event): - testapp.ensure_typical_event() - +def test_ticket_voucher_flow(testapp, upcoming_event, ticket_types): # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') receiver_token, refresh = testapp.auth_get_tokens('jeff@example.com', 'sixcharacters') @@ -14,12 +12,8 @@ def test_ticket_voucher_flow(testapp, upcoming_event): receiver_user = testapp.get_user(receiver_token) third_party_user = testapp.get_user(third_party_token) - # Current event - current_event = testapp.get('/event/current', status=200).json_body - assert current_event['uuid'] is not None - # Get existing ticket types - res = testapp.get('/event/%s/ticketType' % current_event['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) ticket_type = res.json_body[0] @@ -60,7 +54,8 @@ def test_ticket_voucher_flow(testapp, upcoming_event): voucher = testapp.post_json('/ticket_voucher', dict({ 'ticket_type_uuid': ticket_type['uuid'], 'recipient_user_uuid': receiver_user['uuid'], - 'last_use_event_uuid': current_event['uuid'] + 'last_use_event_uuid': str(upcoming_event.uuid), + 'event_brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body @@ -103,7 +98,7 @@ def test_ticket_voucher_flow(testapp, upcoming_event): assert len(jeff_owned_tickets_pre) + 1 == len(jeff_owned_tickets_post) assert post_burn_voucher['ticket'] is not None - assert post_burn_voucher['ticket']['event']['uuid'] == current_event['uuid'] + assert post_burn_voucher['ticket']['event']['uuid'] == str(upcoming_event.uuid) # Assert the ticket voucher is now burned jeff_owned_vouchers = testapp.get('/user/%s/ticket_vouchers' % receiver_user['uuid'], headers=dict({ @@ -112,9 +107,7 @@ def test_ticket_voucher_flow(testapp, upcoming_event): assert len(jeff_owned_vouchers) == 1 assert jeff_owned_vouchers[0]['is_used'] == True -def test_expired_voucher_flow(testapp, db, upcoming_event): - testapp.ensure_typical_event() - +def test_expired_voucher_flow(testapp, db, upcoming_event, ticket_types): # test is an admin sender_token, refresh = testapp.auth_get_tokens('test@example.com', 'sixcharacters') receiver_token, refresh = testapp.auth_get_tokens('jeff@example.com', 'sixcharacters') @@ -122,15 +115,11 @@ def test_expired_voucher_flow(testapp, db, upcoming_event): sender_user = testapp.get_user(sender_token) receiver_user = testapp.get_user(receiver_token) - # Current event - current_event = testapp.get('/event/current', status=200).json_body - assert current_event['uuid'] is not None - # Find an old event old_event = db.query(Event).filter(Event.end_time < datetime.now()).first() # Get existing ticket types - res = testapp.get('/event/%s/ticketType' % current_event['uuid'], headers=dict({ + res = testapp.get('/event/%s/ticketType' % upcoming_event.uuid, headers=dict({ "Authorization": "Bearer " + sender_token }), status=200) ticket_type = res.json_body[0] @@ -145,6 +134,7 @@ def test_expired_voucher_flow(testapp, db, upcoming_event): 'ticket_type_uuid': ticket_type['uuid'], 'recipient_user_uuid': receiver_user['uuid'], 'last_use_event_uuid': str(old_event.uuid), + 'event_brand_uuid': str(upcoming_event.event_brand.uuid) }), headers=dict({ "Authorization": "Bearer " + sender_token }), status=200).json_body diff --git a/phoenixRest/views/agenda/__init__.py b/phoenixRest/views/agenda/__init__.py index 2a1ba58..3e51018 100644 --- a/phoenixRest/views/agenda/__init__.py +++ b/phoenixRest/views/agenda/__init__.py @@ -21,7 +21,6 @@ @resource(name='agenda') class AgendaViews(object): __acl__ = [ - (Allow, Everyone, 'get'), (Allow, ADMIN, 'create'), (Allow, EVENT_ADMIN, 'create'), (Allow, INFO_ADMIN, 'create'), @@ -36,16 +35,6 @@ def __getitem__(self, key): node.__name__ = key return node - - -@view_config(context=AgendaViews, request_method='GET', renderer='json', permission='get') -def get_agenda_entries(request): - # Find all events and sort them by start time - entries = request.db.query(AgendaEntry).filter(AgendaEntry.event == get_current_event(request)).order_by(AgendaEntry.time.asc()).all() - return entries - - - @view_config(context=AgendaViews, request_method='PUT', renderer='json', permission='create') @validate(json_body={'event_uuid': str, 'title': str, 'description': str, 'location': str, 'time': int, 'duration': int, 'pinned': bool}) def create_agenda_entry(context, request): diff --git a/phoenixRest/views/application/__init__.py b/phoenixRest/views/application/__init__.py index 6042f67..669c86a 100644 --- a/phoenixRest/views/application/__init__.py +++ b/phoenixRest/views/application/__init__.py @@ -7,7 +7,7 @@ from sqlalchemy import and_ from sqlalchemy.orm import joinedload -from phoenixRest.models.core.event import Event, get_current_event +from phoenixRest.models.core.event import Event, get_current_events from phoenixRest.models.core.user import User from phoenixRest.models.core.avatar import Avatar @@ -77,7 +77,7 @@ def get_applications_mine(request): return applications @view_config(context=ApplicationViews, name='', request_method='PUT', renderer='json', permission='create') -@validate(json_body={'crews': list, 'contents': str}) +@validate(json_body={'crews': list, 'contents': str, 'event_uuid': str}) def create_application(context, request): if request.user.avatar is None: request.response.status = 400 @@ -105,7 +105,7 @@ def create_application(context, request): crew_list = list(map(lambda crew: request.db.query(Crew).filter(Crew.uuid == crew).first(), request.json_body['crews'])) if None in crew_list: - request.response.status = 404 + request.response.status = 400 return { "error": "Crew not found" } @@ -117,8 +117,20 @@ def create_application(context, request): "error": "You cannot apply to %s" % crew.name } + event = request.db.query(Event).filter(Event.uuid == request.json_body['event_uuid']).first() + if not event: + request.response.status = 400 + return { + "error": "Event not found" + } + # Fetch current event - event = get_current_event(request) + active_events = list(map(lambda u: str(u), get_current_events(request.db))) + if str(event.uuid) not in active_events: + request.response.status = 400 + return { + "error": "Event is not current - you can't create an application for a non-current event" + } application = Application(user=request.user, event=event, diff --git a/phoenixRest/views/application/instance.py b/phoenixRest/views/application/instance.py index 3c05a6f..f7e6cab 100644 --- a/phoenixRest/views/application/instance.py +++ b/phoenixRest/views/application/instance.py @@ -10,7 +10,7 @@ from phoenixRest.models.crew.application import Application, ApplicationState from phoenixRest.models.crew.position import create_or_fetch_crew_position from phoenixRest.models.crew.position_mapping import PositionMapping -from phoenixRest.models.core.event import get_current_event +from phoenixRest.models.core.event import get_current_events from phoenixRest.mappers.application import map_application_with_hidden_fields @@ -84,6 +84,15 @@ def edit_application(context, request): context.applicationInstance.last_processed_by = request.user request.db.add(context.applicationInstance) + + # If the application is not for a current event, it is considered expired and cannot be accepted + active_events = list(map(lambda u: str(u), get_current_events(request.db))) + if str(context.applicationInstance.event.uuid) not in active_events: + request.response.status = 400 + return { + "error": "The application is for an event that is not current, so you cannot approve it" + } + accepted_crew = None if context.applicationInstance.state == ApplicationState.accepted: @@ -111,7 +120,7 @@ def edit_application(context, request): 'error': "Unable to get position" } - mapping = PositionMapping(context.applicationInstance.user, position, get_current_event(request)) + mapping = PositionMapping(context.applicationInstance.user, position, context.applicationInstance.event) position.position_mappings.append(mapping) # Send mail diff --git a/phoenixRest/views/card_order/__init__.py b/phoenixRest/views/card_order/__init__.py index 806b919..fdcb452 100644 --- a/phoenixRest/views/card_order/__init__.py +++ b/phoenixRest/views/card_order/__init__.py @@ -5,7 +5,7 @@ from phoenixRest.resource import resource from phoenixRest.utils import validate -from phoenixRest.models.core.event import Event, get_current_event +from phoenixRest.models.core.event import Event, get_current_events from phoenixRest.models.core.user import User from phoenixRest.models.crew.card_order import CardOrder from phoenixRest.views.card_order.instance import CardOrderInstanceResource @@ -15,11 +15,7 @@ class CardOrderResource(object): __acl__ = [ # Roles with permission to create a new card order (Allow, CHIEF, "create"), - (Allow, ADMIN, "create"), - # Roles with permission to view all card orders for current event - (Allow, CHIEF, "view_all"), - (Allow, CREW_CARD_PRINTER, "view_all"), - (Allow, ADMIN, "view_all"), + (Allow, ADMIN, "create") ] def __init__(self, request): @@ -33,13 +29,20 @@ def __getitem__(self, key): # Creates a new card order @view_config(name="", context=CardOrderResource, request_method="POST", renderer="json", permission="create") -@validate(json_body={"user_uuid": str}) +@validate(json_body={"user_uuid": str, "event_uuid": str}) def create_card_order(context, request): - current_event = get_current_event(request) - if current_event is None: + event = request.db.query(Event).filter(Event.uuid == request.json_body['event_uuid']).first() + if not event: request.response.status = 400 return { - "error": "Current event not found" + "error": "Event not found" + } + + active_events = list(map(lambda u: str(u), get_current_events(request.db))) + if str(event.uuid) not in active_events: + request.response.status = 400 + return { + "error": "Event is not current - you can't create an application for a non-current event" } subject_user_uuid = request.json_body["user_uuid"] @@ -52,25 +55,8 @@ def create_card_order(context, request): creator_user = request.user - card_order = CardOrder(current_event, subject_user, creator_user) + card_order = CardOrder(event, subject_user, creator_user) request.db.add(card_order) request.db.flush() return card_order - -# Get all card orders for specified or current event -@view_config(name="", context=CardOrderResource, request_method="GET", renderer="json", permission="view_all") -def get_card_orders(context, request): - event = None - # We either get the specified event or the current event - if "event_uuid" in request.GET: - event = request.db.query(Event).filter(Event.uuid == request.GET["event_uuid"]).first() - if event is None: - request.response.status = 400 - return { - 'error': "Event not found" - } - else: - event = get_current_event(request) - - return request.db.query(CardOrder).filter(CardOrder.event_uuid == event.uuid).all() diff --git a/phoenixRest/views/card_order/instance.py b/phoenixRest/views/card_order/instance.py index 6041f48..b64b78b 100644 --- a/phoenixRest/views/card_order/instance.py +++ b/phoenixRest/views/card_order/instance.py @@ -54,7 +54,7 @@ def generate_crew_card_from_order(context, request): context.cardOrderInstance.last_updated = datetime.now() context.cardOrderInstance.updated_by_user = request.user - return generate_badge(request, context.cardOrderInstance.subject_user, get_current_event(request)) + return generate_badge(request, context.cardOrderInstance.subject_user, context.cardOrderInstance.event) # Marks the order as finished @view_config(name="finish", context=CardOrderInstanceResource, request_method="PATCH", renderer="json", permission="finish") diff --git a/phoenixRest/views/email/__init__.py b/phoenixRest/views/email/__init__.py index 3bded62..376379e 100644 --- a/phoenixRest/views/email/__init__.py +++ b/phoenixRest/views/email/__init__.py @@ -9,6 +9,7 @@ from phoenixRest.models.core.consent_withdrawal_code import ConsentWithdrawalCode from phoenixRest.models.core.user_consent import UserConsent, ConsentType from phoenixRest.models.core.event import Event, get_current_event +from phoenixRest.models.core.event_brand import EventBrand from phoenixRest.models.tickets.ticket import Ticket from phoenixRest.models.tickets.ticket_voucher import TicketVoucher from phoenixRest.models.tickets.ticket_type import TicketType @@ -41,8 +42,8 @@ class MailType(Enum): crew_info = auto() competition_info = auto() -def get_recipients_by_category(request, category, argument): - current_event = get_current_event(request) +def get_recipients_by_category(request, category, argument, brand): + current_event = get_current_event(request.db, brand) db = request.db current_user_uuid = request.user.uuid @@ -198,10 +199,17 @@ def __init__(self, request): self.request = request @view_config(name='dryrun', context=MarketingResource, request_method='POST', renderer='json', permission='test_email') -@validate(json_body={'recipient_category': str, 'subject': str, 'body': str}) +@validate(json_body={'recipient_category': str, 'subject': str, 'body': str, 'brand_uuid': str}) def test_mail(context, request): """Returns the amount of people who will receive the mail""" + brand = request.db.query(EventBrand).filter(EventBrand.uuid == request.json_body["brand_uuid"]).first() + if not brand: + request.response.status = 400 + return { + "error": "Unknown brand" + } + argument = request.json_body.get('argument') category_str = request.json_body['recipient_category'] @@ -217,7 +225,7 @@ def test_mail(context, request): # Parse the markdown just to ensure no exception is raised. Maybe we do more validation in the future mistune.html(request.json_body['body']) - recipients = get_recipients_by_category(request, category, argument) + recipients = get_recipients_by_category(request, category, argument, brand) # Test that all the info extraction functions also work for recipient in recipients: @@ -227,8 +235,15 @@ def test_mail(context, request): } @view_config(name='send', context=MarketingResource, request_method='POST', renderer='json', permission='test_email') -@validate(json_body={'recipient_category': str, 'subject': str, 'body': str}) +@validate(json_body={'recipient_category': str, 'subject': str, 'body': str, 'brand_uuid': str}) def send_mail(context, request): + brand = request.db.query(EventBrand).filter(EventBrand.uuid == request.json_body["brand_uuid"]).first() + if not brand: + request.response.status = 400 + return { + "error": "Unknown brand" + } + argument = request.json_body.get('argument') category_str = request.json_body['recipient_category'] @@ -243,7 +258,7 @@ def send_mail(context, request): email_subject = request.json_body['subject'] email_body = mistune.html(request.json_body['body']) - recipients = get_recipients_by_category(request, category, argument) + recipients = get_recipients_by_category(request, category, argument, brand) for recipient in recipients: template_file, template_context = get_mail_details_by_category(request, category, recipient) diff --git a/phoenixRest/views/event/__init__.py b/phoenixRest/views/event/__init__.py index 7e2ddbd..e89bb5a 100644 --- a/phoenixRest/views/event/__init__.py +++ b/phoenixRest/views/event/__init__.py @@ -24,8 +24,7 @@ @resource(name='event') class EventViews(object): __acl__ = [ - (Allow, Everyone, 'current::get'), - (Allow, Everyone, 'get'), + (Allow, Everyone, 'list'), (Allow, ADMIN, 'create'), # Authenticated pages @@ -45,11 +44,7 @@ def __getitem__(self, key): node.__name__ = key return node -@view_config(context=EventViews, name='current', request_method='GET', renderer='json', permission='current::get') -def current(request): - return get_current_event(request) - -@view_config(context=EventViews, request_method='GET', renderer='json', permission='get') +@view_config(context=EventViews, request_method='GET', renderer='json', permission='list') def get_events(request): # Find all events and sort them by start time events = request.db.query(Event).order_by(Event.start_time.asc()).all() @@ -64,7 +59,3 @@ def create_event(request): request.db.add(event) request.db.flush() return event - -@view_config(context=EventViews, name='current', request_method='OPTIONS', renderer='string', permission='current::get') -def token_options(request): - return "" diff --git a/phoenixRest/views/event/instance.py b/phoenixRest/views/event/instance.py index d2ef589..574afdf 100644 --- a/phoenixRest/views/event/instance.py +++ b/phoenixRest/views/event/instance.py @@ -1,3 +1,4 @@ +from phoenixRest.roles import CREW_CARD_PRINTER from pyramid.view import view_config, view_defaults from pyramid.httpexceptions import ( HTTPForbidden, @@ -7,8 +8,10 @@ from pyramid.authorization import Authenticated, Everyone, Deny, Allow from phoenixRest.models.core.event import Event +from phoenixRest.models.core.agenda_entry import AgendaEntry from phoenixRest.models.core.user import User from phoenixRest.models.crew.application import Application +from phoenixRest.models.crew.card_order import CardOrder from phoenixRest.models.crew.application_crew_mapping import ApplicationCrewMapping from phoenixRest.models.tickets.ticket import Ticket from phoenixRest.models.tickets.row import Row @@ -24,6 +27,8 @@ from sqlalchemy import and_ from sqlalchemy.orm import joinedload +from datetime import datetime + import logging log = logging.getLogger(__name__) @@ -53,6 +58,14 @@ def __acl__(self): (Allow, CHIEF, 'applications_get'), (Allow, ADMIN, 'applications_get'), + + (Allow, CHIEF, 'list_card_orders'), + (Allow, ADMIN, 'list_card_orders'), + (Allow, CREW_CARD_PRINTER, 'list_card_orders'), + + (Allow, Everyone, 'list_agenda_entries'), + + (Allow, ADMIN, 'event_update'), ] return acl @@ -67,6 +80,120 @@ def __init__(self, request, uuid): def get_event(context, request): return context.eventInstance +@view_config(context=EventInstanceResource, name='', request_method='PATCH', renderer='json', permission='event_update') +def update_event(context, request): + # Validate and update fields that are present in the request body + if not hasattr(request, 'json_body') or not request.json_body: + request.response.status = 400 + return { + 'error': 'Request body must be JSON' + } + + event = context.eventInstance + body = request.json_body + + # Collect timestamp values for cross-field validation + new_booking_time = None + new_start_time = None + new_end_time = None + + # Non-relation fields with validation + if 'name' in body: + if not isinstance(body['name'], str) or not body['name'].strip(): + request.response.status = 400 + return {'error': 'name must be a non-empty string'} + event.name = body['name'] + + if 'booking_time' in body: + if not isinstance(body['booking_time'], int) or body['booking_time'] < 0: + request.response.status = 400 + return {'error': 'booking_time must be a non-negative Unix timestamp (integer)'} + new_booking_time = datetime.fromtimestamp(body['booking_time']) + event.booking_time = new_booking_time + + if 'priority_seating_time_delta' in body: + if not isinstance(body['priority_seating_time_delta'], int) or body['priority_seating_time_delta'] < 0: + request.response.status = 400 + return {'error': 'priority_seating_time_delta must be a non-negative integer (seconds)'} + event.priority_seating_time_delta = body['priority_seating_time_delta'] + + if 'seating_time_delta' in body: + if not isinstance(body['seating_time_delta'], int) or body['seating_time_delta'] < 0: + request.response.status = 400 + return {'error': 'seating_time_delta must be a non-negative integer (seconds)'} + event.seating_time_delta = body['seating_time_delta'] + + if 'start_time' in body: + if not isinstance(body['start_time'], int) or body['start_time'] < 0: + request.response.status = 400 + return {'error': 'start_time must be a non-negative Unix timestamp (integer)'} + new_start_time = datetime.fromtimestamp(body['start_time']) + event.start_time = new_start_time + + if 'end_time' in body: + if not isinstance(body['end_time'], int) or body['end_time'] < 0: + request.response.status = 400 + return {'error': 'end_time must be a non-negative Unix timestamp (integer)'} + new_end_time = datetime.fromtimestamp(body['end_time']) + event.end_time = new_end_time + + if 'theme' in body: + if body['theme'] is not None and not isinstance(body['theme'], str): + request.response.status = 400 + return {'error': 'theme must be a string or null'} + event.theme = body['theme'] + + if 'max_participants' in body: + if not isinstance(body['max_participants'], int) or body['max_participants'] <= 0: + request.response.status = 400 + return {'error': 'max_participants must be a positive integer'} + event.max_participants = body['max_participants'] + + if 'participant_age_limit_inclusive' in body: + if not isinstance(body['participant_age_limit_inclusive'], int) or body['participant_age_limit_inclusive'] < -1: + request.response.status = 400 + return {'error': 'participant_age_limit_inclusive must be an integer >= -1 (-1 means not applicable)'} + event.participant_age_limit_inclusive = body['participant_age_limit_inclusive'] + + if 'crew_age_limit_inclusive' in body: + if not isinstance(body['crew_age_limit_inclusive'], int) or body['crew_age_limit_inclusive'] < -1: + request.response.status = 400 + return {'error': 'crew_age_limit_inclusive must be an integer >= -1 (-1 means not applicable)'} + event.crew_age_limit_inclusive = body['crew_age_limit_inclusive'] + + if 'cancellation_reason' in body: + if body['cancellation_reason'] is not None and not isinstance(body['cancellation_reason'], str): + request.response.status = 400 + return {'error': 'cancellation_reason must be a string or null'} + event.cancellation_reason = body['cancellation_reason'] + + # Seatmap relation - verify it exists + if 'seatmap_uuid' in body: + if body['seatmap_uuid'] is not None: + seatmap = request.db.query(Seatmap).filter(Seatmap.uuid == body['seatmap_uuid']).first() + if not seatmap: + request.response.status = 400 + return {'error': 'Seatmap not found'} + event.seatmap = seatmap + else: + event.seatmap = None + + # Cross-field validation for timestamps + booking_time = new_booking_time if new_booking_time else event.booking_time + start_time = new_start_time if new_start_time else event.start_time + end_time = new_end_time if new_end_time else event.end_time + + if booking_time >= start_time: + request.response.status = 400 + return {'error': 'booking_time must be before start_time'} + + if start_time >= end_time: + request.response.status = 400 + return {'error': 'start_time must be before end_time'} + + request.db.flush() + return event + # Objects relating to the specific event @view_config(context=EventInstanceResource, name='applications', request_method='GET', renderer='json', permission='applications_get') def get_all_applications(context, request): @@ -124,3 +251,16 @@ def get_ticket_types(context, request): # Ticket types assigned to the event static_types = context.eventInstance.static_ticket_types return list(set(row_types+static_types)) + +@view_config(context=EventInstanceResource, name="agenda", request_method='GET', renderer='json', permission='list_agenda_entries') +def get_agenda_entries(context, request): + # Find all events and sort them by start time + entries = request.db.query(AgendaEntry).filter(AgendaEntry.event == context.eventInstance).order_by(AgendaEntry.time.asc()).all() + return entries + + +# Get all card orders for specified or current event +@view_config(name="card_orders", context=EventInstanceResource, request_method="GET", renderer="json", permission="list_card_orders") +def get_card_orders(context, request): + # We either get the specified event or the current event + return request.db.query(CardOrder).filter(CardOrder.event == context.eventInstance).all() diff --git a/phoenixRest/views/event_brand/__init__.py b/phoenixRest/views/event_brand/__init__.py new file mode 100644 index 0000000..530d28c --- /dev/null +++ b/phoenixRest/views/event_brand/__init__.py @@ -0,0 +1,50 @@ +from pyramid.view import view_config, view_defaults +from pyramid.httpexceptions import ( + HTTPForbidden, +) +from pyramid.authorization import Authenticated, Everyone, Deny, Allow + + +from phoenixRest.models.core.event_brand import EventBrand + +from phoenixRest.utils import validate +from phoenixRest.resource import resource +from phoenixRest.views.event_brand.instance import EventBrandInstanceResource + +from phoenixRest.roles import ADMIN + +import logging +log = logging.getLogger(__name__) + + +@resource(name='event_brand') +class EventBrandResource(object): + __acl__ = [ + (Allow, Everyone, 'get'), + (Allow, ADMIN, 'create'), + ] + def __init__(self, request): + self.request = request + + def __getitem__(self, key): + node = EventBrandInstanceResource(self.request, key) + node.__parent__ = self + node.__name__ = key + return node + +@view_config(context=EventBrandResource, request_method='GET', renderer='json', permission='get') +def get_event_brands(context, request): + # Find all events and sort them by start time + event_types = request.db.query(EventBrand).all() + return event_types + +@view_config(context=EventBrandResource, request_method='POST', renderer='json', permission='create') +@validate(json_body={'name': str}) +def create_event_brand(context, request): + # Find all events and sort them by start time + brand = EventBrand(request.json_body['name']) + + request.db.add(brand) + request.db.flush() + + return brand diff --git a/phoenixRest/views/event_brand/instance.py b/phoenixRest/views/event_brand/instance.py new file mode 100644 index 0000000..513b4b5 --- /dev/null +++ b/phoenixRest/views/event_brand/instance.py @@ -0,0 +1,34 @@ +from pyramid.view import view_config, view_defaults +from pyramid.httpexceptions import ( + HTTPForbidden, + HTTPNotFound, + HTTPBadRequest +) +from pyramid.authorization import Authenticated, Everyone, Deny, Allow + +from phoenixRest.models.core.event_brand import EventBrand +from phoenixRest.models.core.event import get_current_event + +class EventBrandInstanceResource(object): + def __acl__(self): + acl = [ + (Allow, Everyone, 'get'), + (Allow, Everyone, 'get_current_event'), + ] + return acl + + def __init__(self, request, uuid): + self.request = request + self.eventBrandInstance = request.db.query(EventBrand).filter(EventBrand.uuid == uuid).first() + + if self.eventBrandInstance is None: + raise HTTPNotFound("Event brand not found") + + +@view_config(context=EventBrandInstanceResource, name='', request_method='GET', renderer='json', permission='get') +def get_event_brand(context, request): + return context.eventBrandInstance + +@view_config(context=EventBrandInstanceResource, name='current_event', request_method='GET', renderer='json', permission='get_current_event') +def get_active_event(context, request): + return get_current_event(request.db, context.eventBrandInstance) diff --git a/phoenixRest/views/payment/__init__.py b/phoenixRest/views/payment/__init__.py index a0382df..6522e94 100644 --- a/phoenixRest/views/payment/__init__.py +++ b/phoenixRest/views/payment/__init__.py @@ -86,7 +86,7 @@ def create_payment(context, request): "error": "You have already created a payment for this card. Please finish it!" } - payment = Payment(request.user, chosen_provider, store_session.get_total(), get_current_event(request)) + payment = Payment(request.user, chosen_provider, store_session.get_total(), store_session.event) payment.store_session = store_session request.db.add(payment) diff --git a/phoenixRest/views/position_mapping/__init__.py b/phoenixRest/views/position_mapping/__init__.py index 9ab5b6a..8c9df79 100644 --- a/phoenixRest/views/position_mapping/__init__.py +++ b/phoenixRest/views/position_mapping/__init__.py @@ -13,6 +13,7 @@ from phoenixRest.roles import ADMIN, MEMBER from phoenixRest.models.core.user import User +from phoenixRest.models.core.event import Event from phoenixRest.models.core.event import get_current_event from phoenixRest.models.crew.position_mapping import PositionMapping @@ -39,7 +40,7 @@ def __getitem__(self, key): return node @view_config(context=PositionMappingResource, name='', request_method='POST', renderer='json', permission='create_position_mapping') -@validate(json_body={'user_uuid': str, 'position_uuid': str}) +@validate(json_body={'user_uuid': str, 'position_uuid': str, 'event_uuid': str}) def create_mapping(context, request): user = request.db.query(User).filter(User.uuid == request.json_body['user_uuid']).first() if user is None: @@ -56,13 +57,18 @@ def create_mapping(context, request): return { "error": "Position not found" } - - current_event = get_current_event(request) + + event = request.db.query(Event).filter(Event.uuid == request.json_body['event_uuid']).first() + if event is None: + request.response.status = 404 + return { + "error": "Event not found" + } existing_mapping = request.db.query(PositionMapping) \ .filter(and_( PositionMapping.user == user, - PositionMapping.event == current_event, + PositionMapping.event == event, PositionMapping.position == position )) \ .first() @@ -73,7 +79,7 @@ def create_mapping(context, request): "error": "User already has a position mapping for the given position and event" } - mapping = PositionMapping(user, position, current_event) + mapping = PositionMapping(user, position, event) position.position_mappings.append(mapping) request.db.add(mapping) diff --git a/phoenixRest/views/seatmap/instance.py b/phoenixRest/views/seatmap/instance.py index b526577..a5b10ac 100644 --- a/phoenixRest/views/seatmap/instance.py +++ b/phoenixRest/views/seatmap/instance.py @@ -64,17 +64,21 @@ def get_seatmap(context, request): return context.seatmapInstance @view_config(context=SeatmapInstanceViews, name='availability', request_method='GET', renderer='json', permission='seatmap_get_availability') +@validate(get={"event_uuid": str}) def get_seatmap_availability(context, request): - event = None - if 'event_uuid' in request.GET: - event = request.db.query(Event).filter(Event.uuid == request.GET['event_uuid']).first() - if event is None: - request.response.status = 404 - return { - 'error': "Event not found" - } - else: - event = get_current_event(request) + event = request.db.query(Event).filter(Event.uuid == request.GET['event_uuid']).first() + if event is None: + request.response.status = 400 + return { + 'error': "Event not found" + } + + if not event.seatmap or event.seatmap.uuid != context.seatmapInstance.uuid: + request.response.status = 400 + return { + 'error': "Specified event does not use this seatmap" + } + seatmap = request.db.query(Seatmap) \ .join(Row, Row.seatmap_uuid == Seatmap.uuid) \ diff --git a/phoenixRest/views/store_session/__init__.py b/phoenixRest/views/store_session/__init__.py index 44847e8..f6ce4b8 100644 --- a/phoenixRest/views/store_session/__init__.py +++ b/phoenixRest/views/store_session/__init__.py @@ -6,11 +6,12 @@ from pyramid.authorization import Authenticated, Everyone, Deny, Allow -from phoenixRest.models.core.event import get_current_event +from phoenixRest.models.core.event import get_current_events, Event from phoenixRest.models.tickets.store_session import StoreSession from phoenixRest.models.tickets.store_session_cart_entry import StoreSessionCartEntry from phoenixRest.models.tickets.ticket_type import TicketType +from phoenixRest.utils import validate from phoenixRest.resource import resource from phoenixRest.roles import ADMIN, TICKET_BYPASS_TICKETSALE_START_RESTRICTION, TICKET_WHOLESALE, TICKET_ADMIN @@ -49,10 +50,25 @@ def get_active_sessions(request): return request.db.query(StoreSession).filter(StoreSession.expires > datetime.now()).order_by(StoreSession.created).all() @view_config(context=StoreSessionResource, name='', request_method='PUT', renderer='json', permission='create') +@validate(json_body={'event_uuid': str}) def create_store_session(context, request): max_purchase_amt = int(request.registry.settings["ticket.max_purchase_amt"]) store_session_lifetime = int(request.registry.settings["ticket.store_session_lifetime"]) - event = get_current_event(request) + + event = request.db.query(Event).filter(Event.uuid == request.json_body["event_uuid"]).first() + if event is None: + request.response.status = 400 + return { + "error": "Event not found" + } + + active_events = list(map(lambda u: str(u), get_current_events(request.db))) + if str(event.uuid) not in active_events: + request.response.status = 400 + return { + "error": f"Event is not current - you can't create a store session for a non-curent event" + } + if datetime.now() < event.booking_time and TICKET_BYPASS_TICKETSALE_START_RESTRICTION not in request.effective_principals: request.response.status = 400 @@ -78,7 +94,7 @@ def create_store_session(context, request): } # Expiry is prolonged when the user is sent to external payment, as some payment providers implement their own mechanism for this(vipps) - store_session = StoreSession(request.user, store_session_lifetime) + store_session = StoreSession(request.user, store_session_lifetime, event) total_qty = 0 for entry in request.json_body['cart']: diff --git a/phoenixRest/views/ticket/__init__.py b/phoenixRest/views/ticket/__init__.py index 23defb5..4131e93 100644 --- a/phoenixRest/views/ticket/__init__.py +++ b/phoenixRest/views/ticket/__init__.py @@ -8,8 +8,9 @@ from phoenixRest.models.tickets.ticket_type import TicketType from phoenixRest.models.core.user import User +from phoenixRest.models.core.event import Event from phoenixRest.models.tickets.ticket import Ticket -from phoenixRest.models.core.event import get_current_event +from phoenixRest.models.core.event import get_current_events from phoenixRest.utils import validate from phoenixRest.resource import resource @@ -50,7 +51,7 @@ def get_all_tickets(context, request): return request.db.query(Ticket).order_by(Ticket.ticket_id).all() @view_config(name='', context=TicketResource, request_method='POST', renderer='json', permission='create') -@validate(json_body={'recipient': str, 'ticket_type': str}) +@validate(json_body={'recipient': str, 'ticket_type': str, 'event_uuid': str}) def create_ticket(context, request): receiving_user = request.db.query(User).filter(User.uuid == request.json_body['recipient']).first() if not receiving_user: @@ -66,7 +67,22 @@ def create_ticket(context, request): "error": "Ticket type not found" } - ticket = Ticket(receiving_user, None, ticket_type, get_current_event(request)) + event = request.db.query(Event).filter(Event.uuid == request.json_body["event_uuid"]).first() + if event is None: + request.response.status = 400 + return { + "error": "Event not found" + } + + active_events = list(map(lambda u: str(u), get_current_events(request.db))) + if str(event.uuid) not in active_events: + request.response.status = 400 + return { + "error": f"Event is not current - you can't create a ticket for a non-curent event" + } + + + ticket = Ticket(receiving_user, None, ticket_type, event) request.db.add(ticket) request.db.flush() diff --git a/phoenixRest/views/ticket/instance.py b/phoenixRest/views/ticket/instance.py index a0e2264..d85338e 100644 --- a/phoenixRest/views/ticket/instance.py +++ b/phoenixRest/views/ticket/instance.py @@ -8,7 +8,7 @@ from pyramid.authorization import Authenticated, Everyone, Deny, Allow from phoenixRest.models.core.user import User -from phoenixRest.models.core.event import get_current_event +from phoenixRest.models.core.event import get_current_events from phoenixRest.models.tickets.ticket_transfer import TicketTransfer from phoenixRest.models.tickets.ticket import Ticket from phoenixRest.models.tickets.seat import Seat @@ -76,8 +76,21 @@ def get_ticket(context, request): @validate(json_body={'seat_uuid': str}) def seat_ticket(context, request): seat = request.db.query(Seat).filter(Seat.uuid == request.json_body['seat_uuid']).first() - event = get_current_event(request) + event = context.ticketInstance.event + + active_events = list(map(lambda u: str(u), get_current_events(request.db))) + if str(event.uuid) not in active_events: + request.response.status = 400 + return { + "error": "You cannot seat a ticket for an event that is not current" + } + if event.seatmap_uuid != seat.row.seatmap.uuid: + request.response.status = 400 + return { + "error": "The seat belongs to a different seatmap than that of the event the ticket belongs to" + } + seating_time = event.booking_time + timedelta(seconds=event.seating_time_delta) if not context.ticketInstance.ticket_type.seatable: diff --git a/phoenixRest/views/ticket_voucher/__init__.py b/phoenixRest/views/ticket_voucher/__init__.py index 3012bfa..9d38f26 100644 --- a/phoenixRest/views/ticket_voucher/__init__.py +++ b/phoenixRest/views/ticket_voucher/__init__.py @@ -6,6 +6,7 @@ from phoenixRest.models.core.user import User from phoenixRest.models.core.event import Event +from phoenixRest.models.core.event import EventBrand from phoenixRest.models.tickets.ticket_voucher import TicketVoucher from phoenixRest.models.tickets.ticket_type import TicketType @@ -44,30 +45,37 @@ def get_vouchers(context, request): return request.db.query(TicketVoucher).all() @view_config(name='', context=TicketVoucherResource, request_method='POST', renderer='json', permission='create') -@validate(json_body={'recipient_user_uuid': str, 'ticket_type_uuid': str, 'last_use_event_uuid': str}) +@validate(json_body={'recipient_user_uuid': str, 'ticket_type_uuid': str, 'last_use_event_uuid': str, 'event_brand_uuid': str}) def create_voucher(context, request): recipient_user = request.db.query(User).filter(User.uuid == request.json_body['recipient_user_uuid']).first() if not recipient_user: - request.response.status = 404 + request.response.status = 400 return { "error": "recipient_user not found" } ticket_type = request.db.query(TicketType).filter(TicketType.uuid == request.json_body['ticket_type_uuid']).first() if not ticket_type: - request.response.status = 404 + request.response.status = 400 return { "error": "ticket_type not found" } last_use_event = request.db.query(Event).filter(Event.uuid == request.json_body['last_use_event_uuid']).first() if not last_use_event: - request.response.status = 404 + request.response.status = 400 return { "error": "last_use_event not found" } + + brand = request.db.query(EventBrand).filter(EventBrand.uuid == request.json_body['event_brand_uuid']).first() + if not brand: + request.response.status = 400 + return { + "error": "Brand not found" + } - voucher = TicketVoucher(request.user, recipient_user, ticket_type, last_use_event) + voucher = TicketVoucher(request.user, recipient_user, ticket_type, brand, last_use_event) request.db.add(voucher) request.db.flush() diff --git a/phoenixRest/views/ticket_voucher/instance.py b/phoenixRest/views/ticket_voucher/instance.py index 14ff105..2495b29 100644 --- a/phoenixRest/views/ticket_voucher/instance.py +++ b/phoenixRest/views/ticket_voucher/instance.py @@ -60,7 +60,7 @@ def burn_voucher(context, request): # Mint the ticket! context.ticketVoucherInstance.used = datetime.now() - ticket = Ticket(context.ticketVoucherInstance.recipient_user, None, context.ticketVoucherInstance.ticket_type, get_current_event(request)) + ticket = Ticket(context.ticketVoucherInstance.recipient_user, None, context.ticketVoucherInstance.ticket_type, get_current_event(request.db, context.ticketVoucherInstance.event_brand)) context.ticketVoucherInstance.ticket = ticket # Save it diff --git a/phoenixRest/views/user/instance/__init__.py b/phoenixRest/views/user/instance/__init__.py index 49300ca..ac6e311 100644 --- a/phoenixRest/views/user/instance/__init__.py +++ b/phoenixRest/views/user/instance/__init__.py @@ -338,19 +338,15 @@ def get_owned_tickets(context, request): def get_ticket_vouchers(context, request): return request.db.query(TicketVoucher).filter(TicketVoucher.recipient_user == context.userInstance).all() -# We only care about transfers from this event @view_config(context=UserInstanceResource, name='ticket_transfers', request_method='GET', renderer='json', permission='user_list_ticket_transfers') +@validate(get={"event_uuid": str}) def get_ticket_transfers(context, request): - event = None - if 'event_uuid' in request.GET: - event = request.db.query(Event).filter(Event.uuid == request.GET['event_uuid']).first() - if event is None: - request.response.status = 404 - return { - 'error': "Event not found" - } - else: - event = get_current_event(request) + event = request.db.query(Event).filter(Event.uuid == request.GET['event_uuid']).first() + if event is None: + request.response.status = 404 + return { + 'error': "Event not found" + } transfers = request.db.query(TicketTransfer).filter(and_(TicketTransfer.ticket.has(Ticket.event_uuid == event.uuid), or_( or_(TicketTransfer.from_user == context.userInstance), @@ -611,7 +607,7 @@ def create_discord_mapping_oauth_url(context, request): # Generates a crew card @view_config(context=UserInstanceResource, name='crew_card', request_method='GET', renderer='pillow', permission='get_crew_card') def create_crew_card(context, request): - return generate_badge(request, context.userInstance, get_current_event(request)) + return generate_badge(request, context.userInstance, get_current_event(request.db)) @view_config(context=UserInstanceResource, name='applications', request_method='GET', renderer='json', permission='get_applications') def get_applications(context, request): diff --git a/phoenixRest/views/user/oauth.py b/phoenixRest/views/user/oauth.py index 9700e6d..6e97065 100644 --- a/phoenixRest/views/user/oauth.py +++ b/phoenixRest/views/user/oauth.py @@ -9,7 +9,7 @@ from phoenixRest.models.core.user import User from phoenixRest.models.crew.position_mapping import PositionMapping from phoenixRest.models.crew.position import Position -from phoenixRest.models.core.event import get_current_event +from phoenixRest.models.core.event import get_current_events from phoenixRest.models.core.oauth.oauthCode import OauthCode from phoenixRest.models.core.oauth.refreshToken import OauthRefreshToken @@ -25,13 +25,13 @@ def generate_token(user: User, request): # We now need to fetch the users permissions # https://stackoverflow.com/questions/952914/how-to-make-a-flat-list-out-of-list-of-lists # Extract positions that are for current event, or that are lifetime - current_event = get_current_event(request) + current_events = get_current_events(request.db) current_positions = request.db.query(Position).join(PositionMapping).filter(and_( PositionMapping.user == user, or_( PositionMapping.event == None, - PositionMapping.event == current_event + PositionMapping.event_uuid.in_(current_events) ) )).all()