Skip to content

Commit

Permalink
Link objects to reservation occurrences
Browse files Browse the repository at this point in the history
  • Loading branch information
OmeGak committed Jan 19, 2024
1 parent 2c6cecb commit 851b553
Show file tree
Hide file tree
Showing 18 changed files with 232 additions and 139 deletions.
@@ -0,0 +1,121 @@
"""Link events to reservation occurrences.
Revision ID: b37cbc4bb129
Revises: e2b69fe5155d
Create Date: 2023-12-22 14:08:45.669303
"""

from enum import Enum

import sqlalchemy as sa
from alembic import op

from indico.core.db.sqlalchemy import PyIntEnum


# revision identifiers, used by Alembic.
revision = 'b37cbc4bb129'
down_revision = 'e2b69fe5155d'
branch_labels = None
depends_on = None


class _LinkType(int, Enum):
category = 1
event = 2
contribution = 3
subcontribution = 4
session = 5
session_block = 6


excluded_link_types = {_LinkType.category, _LinkType.subcontribution, _LinkType.session}


def upgrade():
# Create reservation occurrence links
op.create_table(
'reservation_occurrence_links',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('link_type', PyIntEnum(_LinkType, exclude_values=excluded_link_types), nullable=False),
sa.Column('event_id', sa.Integer(), nullable=True),
sa.Column('linked_event_id', sa.Integer(), nullable=True),
sa.Column('session_block_id', sa.Integer(), nullable=True),
sa.Column('contribution_id', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['contribution_id'], ['events.contributions.id']),
sa.ForeignKeyConstraint(['event_id'], ['events.events.id']),
sa.ForeignKeyConstraint(['linked_event_id'], ['events.events.id']),
sa.ForeignKeyConstraint(['session_block_id'], ['events.session_blocks.id']),
sa.CheckConstraint('(event_id IS NULL) = (link_type = 1)', name='valid_event_id'),
sa.CheckConstraint('link_type != 2 '
'OR (contribution_id IS NULL AND session_block_id IS NULL AND linked_event_id IS NOT NULL)',
name='valid_event_link'),
sa.CheckConstraint('link_type != 3 '
'OR (linked_event_id IS NULL AND session_block_id IS NULL AND contribution_id IS NOT NULL)',
name='valid_contribution_link'),
sa.CheckConstraint('link_type != 6 '
'OR (contribution_id IS NULL AND linked_event_id IS NULL AND session_block_id IS NOT NULL)',
name='valid_session_block_link'),
schema='roombooking'
)
op.add_column('reservation_occurrences', sa.Column('link_id', sa.Integer(), nullable=True), schema='roombooking')
op.create_foreign_key(None, 'reservation_occurrences', 'reservation_occurrence_links', ['link_id'], ['id'],
source_schema='roombooking', referent_schema='roombooking')
op.create_index(None, 'reservation_occurrence_links', ['contribution_id'], unique=False, schema='roombooking')
op.create_index(None, 'reservation_occurrence_links', ['event_id'], unique=False, schema='roombooking')
op.create_index(None, 'reservation_occurrence_links', ['linked_event_id'], unique=False, schema='roombooking')
op.create_index(None, 'reservation_occurrence_links', ['session_block_id'], unique=False, schema='roombooking')
op.create_index(None, 'reservation_occurrences', ['link_id'], unique=False, schema='roombooking')

# Migrate data
# TODO

# Drop reservation links
op.drop_index('ix_reservations_link_id', table_name='reservations', schema='roombooking')
op.drop_constraint('fk_reservations_link_id_reservation_links', 'reservations', schema='roombooking')
op.drop_table('reservation_links', schema='roombooking')
op.drop_column('reservations', 'link_id', schema='roombooking')


def downgrade():
# Recreate reservation links
op.add_column('reservations', sa.Column('link_id', sa.INTEGER(), nullable=True),
schema='roombooking')
op.create_foreign_key(None, 'reservations', 'reservation_links', ['link_id'], ['id'],
source_schema='roombooking', referent_schema='roombooking')
op.create_table(
'reservation_links',
sa.Column('id', sa.INTEGER(), nullable=False),
sa.Column('link_type', sa.SMALLINT(), nullable=False),
sa.Column('event_id', sa.INTEGER(), nullable=True),
sa.Column('linked_event_id', sa.INTEGER(), nullable=True),
sa.Column('session_block_id', sa.INTEGER(), nullable=True),
sa.Column('contribution_id', sa.INTEGER(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['contribution_id'], ['events.contributions.id']),
sa.ForeignKeyConstraint(['event_id'], ['events.events.id']),
sa.ForeignKeyConstraint(['linked_event_id'], ['events.events.id']),
sa.ForeignKeyConstraint(['session_block_id'], ['events.session_blocks.id']),
sa.CheckConstraint('(event_id IS NULL) = (link_type = 1)', name='valid_event_id'),
sa.CheckConstraint('(link_type <> 2) '
'OR (contribution_id IS NULL AND session_block_id IS NULL AND linked_event_id IS NOT NULL)',
name='valid_event_link'),
sa.CheckConstraint('(link_type <> 3) '
'OR (linked_event_id IS NULL AND session_block_id IS NULL AND contribution_id IS NOT NULL)',
name='valid_contribution_link'),
sa.CheckConstraint('(link_type <> 6) '
'OR (contribution_id IS NULL AND linked_event_id IS NULL AND session_block_id IS NOT NULL)',
name='valid_session_block_link'),
schema='roombooking'
)

# Migrate data
# TODO

# Drop reservation occurrence links
op.drop_index('ix_reservation_occurrences_link_id', table_name='reservation_occurrences', schema='roombooking')
op.drop_constraint('fk_reservation_occurrences_link_id_reservation_occurrence_links', 'reservation_occurrences',
schema='roombooking')
op.drop_column('reservation_occurrences', 'link_id', schema='roombooking')
op.drop_table('reservation_occurrence_links', schema='roombooking')
17 changes: 8 additions & 9 deletions indico/modules/categories/controllers/management.py
Expand Up @@ -36,7 +36,7 @@
from indico.modules.events.notifications import notify_move_request_closure, notify_move_request_creation
from indico.modules.events.operations import create_event_request
from indico.modules.logs.models.entries import CategoryLogRealm, LogKind
from indico.modules.rb.models.reservations import Reservation, ReservationLink
from indico.modules.rb.models.reservation_occurrences import ReservationOccurrence, ReservationOccurrenceLink
from indico.modules.users import User
from indico.util.fs import secure_filename
from indico.util.i18n import _, ngettext
Expand Down Expand Up @@ -422,15 +422,14 @@ class RHDeleteEvents(RHManageCategorySelectedEventsBase):
def _process(self):
is_submitted = 'confirmed' in request.form
if not is_submitted:
# find out which active bookings are linked to the events in question
num_bookings = (Reservation.query
.join(ReservationLink)
.join(Event, Event.id == ReservationLink.linked_event_id)
.filter(Event.id.in_(e.id for e in self.events),
Reservation.is_pending | Reservation.is_accepted)
.count())
# find out which active booking occurrences are linked to the events in question
num_booking_occurrences = (ReservationOccurrence.query
.join(ReservationOccurrenceLink)
.join(Event, Event.id == ReservationOccurrenceLink.linked_event_id)
.filter(Event.id.in_(e.id for e in self.events), ReservationOccurrence.is_valid)
.count())
return jsonify_template('events/management/delete_events.html',
events=self.events, num_bookings=num_bookings)
events=self.events, num_booking_occurrences=num_booking_occurrences)
for ev in self.events[:]:
ev.delete('Bulk-deleted by category manager', session.user)
flash(ngettext('You have deleted one event', 'You have deleted {} events', len(self.events))
Expand Down
14 changes: 0 additions & 14 deletions indico/modules/events/api.py
Expand Up @@ -7,7 +7,6 @@

import fnmatch
import re
from collections import defaultdict
from datetime import datetime
from hashlib import md5
from operator import attrgetter
Expand Down Expand Up @@ -35,7 +34,6 @@
from indico.modules.events.sessions.models.sessions import Session
from indico.modules.events.timetable.legacy import TimetableSerializer
from indico.modules.events.timetable.models.entries import TimetableEntry
from indico.modules.rb.models.reservation_occurrences import ReservationOccurrence
from indico.util.date_time import iterdays
from indico.util.i18n import orig_string
from indico.util.signals import values_from_signal
Expand Down Expand Up @@ -281,24 +279,12 @@ def _build_event_api_data_base(self, event):
'references': [self.serialize_reference(x) for x in event.references]
}

def _serialize_reservations(self, reservations):
res = defaultdict(list)
for resv in reservations:
occurrences = (resv.occurrences
.filter(ReservationOccurrence.is_valid)
.options(ReservationOccurrence.NO_RESERVATION_USER_STRATEGY)
.all())
res[resv.room.full_name] += [{'startDateTime': occ.start_dt, 'endDateTime': occ.end_dt}
for occ in occurrences]
return res

def _build_session_event_api_data(self, event):
data = self._build_event_api_data_base(event)
data.update({
'_fossil': 'conference',
'adjustedStartDate': self._serialize_date(event.start_dt_local),
'adjustedEndDate': self._serialize_date(event.end_dt_local),
'bookedRooms': self._serialize_reservations(event.reservations),
'supportInfo': {
'_fossil': 'supportInfo',
'caption': event.contact_title,
Expand Down
1 change: 0 additions & 1 deletion indico/modules/events/client/js/creation.js
Expand Up @@ -207,7 +207,6 @@ import {camelizeKeys} from 'indico/utils/case';
!('room_id' in roomData) ||
!startDt.isValid() ||
!endDt.isValid() ||
!startDt.isSame(endDt, 'day') ||
startDt.isSameOrAfter(endDt) ||
isCategoryExcluded(category['id']) ||
timezone !== options.serverDefaultTimezone ||
Expand Down
Expand Up @@ -371,7 +371,7 @@ def _paper_last_revision(cls):
# - editables (Editable.contribution)
# - legacy_mapping (LegacyContributionMapping.contribution)
# - note (EventNote.contribution)
# - room_reservation_links (ReservationLink.contribution)
# - room_reservation_occurrence_links (ReservationOccurrenceLink.contribution)
# - timetable_entry (TimetableEntry.contribution)
# - vc_room_associations (VCRoomEventAssociation.linked_contrib)

Expand Down
2 changes: 1 addition & 1 deletion indico/modules/events/management/controllers/settings.py
Expand Up @@ -51,7 +51,7 @@ def _check_access(self):
def _process(self):
show_booking_warning = False
if (config.ENABLE_ROOMBOOKING and rb_check_if_visible(session.user)
and not self.event.has_ended and self.event.room and not self.event.room_reservation_links):
and not self.event.has_ended and self.event.room and not self.event.room_reservation_occurrence_links):
# Check if any of the managers of the event already have a booking that overlaps with the event datetime
manager_ids = [p.user.id for p in self.event.acl_entries if p.user]
has_overlap = (ReservationOccurrence.query
Expand Down
4 changes: 2 additions & 2 deletions indico/modules/events/management/templates/delete_event.html
@@ -1,11 +1,11 @@
{% from 'confirmation_dialog.html' import confirmation_dialog %}

{% set num_bookings = event.all_room_reservation_links.count() %}
{% set num_bookings = event.all_room_reservation_occurrence_links.count() %}
{% set confirmation_message %}
{% if num_bookings %}
{% trans strong='<strong>'|safe, endstrong='</strong>'|safe -%}
Please note that if you delete the event you will lose all the information contained in it,
{{ strong }}including {{ num_bookings }} Room Booking(s){{ endstrong }} linked to it.
{{ strong }}including {{ num_bookings }} Room Booking Occurrences(s){{ endstrong }} linked to it.
This operation is irreversible!
{%- endtrans %}
{% else %}
Expand Down
4 changes: 2 additions & 2 deletions indico/modules/events/management/templates/delete_events.html
Expand Up @@ -3,10 +3,10 @@
{% set ok_text=_("Delete events") %}

{% set confirmation_message %}
{% if num_bookings %}
{% if num_booking_occurrences %}
{% trans strong='<strong>'|safe, endstrong='</strong>'|safe -%}
Please note that if you delete the events you will lose all the information contained in them,
{{ strong }}including {{ num_bookings }} Room Booking(s){{ endstrong }}.
{{ strong }}including {{ num_booking_occurrences }} Room Booking Occurrences(s){{ endstrong }}.
This operation is irreversible!
{%- endtrans %}
{% else %}
Expand Down
8 changes: 2 additions & 6 deletions indico/modules/events/models/events.py
Expand Up @@ -401,7 +401,7 @@ def __table_args__(cls):
# - all_legacy_attachment_folder_mappings (LegacyAttachmentFolderMapping.event)
# - all_legacy_attachment_mappings (LegacyAttachmentMapping.event)
# - all_notes (EventNote.event)
# - all_room_reservation_links (ReservationLink.event)
# - all_room_reservation_occurrence_links (ReservationOccurrenceLink.event)
# - all_vc_room_associations (VCRoomEventAssociation.event)
# - attachment_folders (AttachmentFolder.linked_event)
# - clones (Event.cloned_from)
Expand Down Expand Up @@ -435,7 +435,7 @@ def __table_args__(cls):
# - reminders (EventReminder.event)
# - requests (Request.event)
# - roles (EventRole.event)
# - room_reservation_links (ReservationLink.linked_event)
# - room_reservation_occurrence_links (ReservationOccurrenceLink.linked_event)
# - session_types (SessionType.event)
# - sessions (Session.event)
# - settings (EventSetting.event)
Expand Down Expand Up @@ -1044,10 +1044,6 @@ def cfp(self):
from indico.modules.events.papers.models.call_for_papers import CallForPapers
return CallForPapers(self)

@property
def reservations(self):
return [link.reservation for link in self.all_room_reservation_links]

@property
def has_ended(self):
return self.end_dt <= now_utc()
Expand Down
2 changes: 1 addition & 1 deletion indico/modules/events/sessions/models/blocks.py
Expand Up @@ -72,7 +72,7 @@ def __table_args__(cls):
# relationship backrefs:
# - contributions (Contribution.session_block)
# - legacy_mapping (LegacySessionBlockMapping.session_block)
# - room_reservation_links (ReservationLink.session_block)
# - room_reservation_occurrence_links (ReservationOccurrenceLink.session_block)
# - session (Session.blocks)
# - timetable_entry (TimetableEntry.session_block)
# - vc_room_associations (VCRoomEventAssociation.linked_block)
Expand Down
18 changes: 9 additions & 9 deletions indico/modules/rb/__init__.py
Expand Up @@ -80,7 +80,7 @@ def _topmenu_items(sender, **kwargs):
@signals.menu.items.connect_via('event-management-sidemenu')
def _sidemenu_items(sender, event, **kwargs):
if config.ENABLE_ROOMBOOKING and event.can_manage(session.user):
yield SideMenuItem('room_booking', _('Room booking'), url_for('rb.event_booking_list', event), 50,
yield SideMenuItem('room_booking', _('Room bookings'), url_for('rb.event_booking_list', event), 50,
icon='location')


Expand All @@ -101,14 +101,14 @@ def _merge_users(target, source, **kwargs):

@signals.event.deleted.connect
def _event_deleted(event, user, **kwargs):
from indico.modules.rb.models.reservations import Reservation
reservation_links = (event.all_room_reservation_links
.join(Reservation)
.filter(~Reservation.is_rejected, ~Reservation.is_cancelled)
.all())
for link in reservation_links:
if link.reservation.end_dt >= datetime.now():
link.reservation.cancel(user or session.user, 'Associated event was deleted')
from indico.modules.rb.models.reservations import ReservationOccurrence
reservation_occurrence_links = (event.all_room_reservation_occurrence_links
.join(ReservationOccurrence)
.filter(~ReservationOccurrence.is_rejected, ~ReservationOccurrence.is_cancelled)
.all())
for link in reservation_occurrence_links:
if link.reservation_occurrence.end_dt >= datetime.now():
link.reservation_occurrence.cancel(user or session.user, 'Associated event was deleted')


class BookPermission(ManagementPermission):
Expand Down
3 changes: 2 additions & 1 deletion indico/modules/rb/controllers/backend/bookings.py
Expand Up @@ -157,7 +157,8 @@ def _link_booking(self, booking, type_, id_, link_back):
obj = get_linked_object(type_, id_)
if obj is None or not obj.event.can_manage(session.user):
return
booking.linked_object = obj
for occurrence in booking.occurrences:
occurrence.linked_object = obj
if link_back:
obj.inherit_location = False
obj.room = self.room
Expand Down
12 changes: 6 additions & 6 deletions indico/modules/rb/event/controllers.py
Expand Up @@ -16,7 +16,7 @@
from indico.modules.events.sessions.models.sessions import Session
from indico.modules.events.timetable import TimetableEntry
from indico.modules.rb.event.forms import BookingListForm
from indico.modules.rb.models.reservations import Reservation, ReservationLink
from indico.modules.rb.models.reservation_occurrences import ReservationOccurrence, ReservationOccurrenceLink
from indico.modules.rb.util import get_booking_params_for_event, rb_check_if_visible
from indico.modules.rb.views import WPEventBookingList
from indico.util.date_time import format_datetime, now_utc
Expand Down Expand Up @@ -52,13 +52,13 @@ def _process(self):
has_contribs = _contrib_query(self.event).has_rows()
has_session_blocks = _session_block_query(self.event).has_rows()

links = (ReservationLink.query.with_parent(self.event)
.options(joinedload('reservation').joinedload('room'),
links = (ReservationOccurrenceLink.query.with_parent(self.event)
.options(joinedload('reservation_occurrence').joinedload('reservation').joinedload('room'),
joinedload('session_block'),
joinedload('contribution'))
.filter(~ReservationLink.reservation.has(Reservation.is_cancelled))
.join(Reservation)
.order_by(Reservation.start_dt)
.filter(~ReservationOccurrenceLink.reservation_occurrence.has(ReservationOccurrence.is_cancelled))
.join(ReservationOccurrence)
.order_by(ReservationOccurrence.start_dt)
.all())

contribs_data = {c.id: {'start_dt': c.start_dt.isoformat(), 'end_dt': c.end_dt.isoformat()}
Expand Down

0 comments on commit 851b553

Please sign in to comment.