diff --git a/livesync/indico_livesync/base.py b/livesync/indico_livesync/base.py index ee0e366d0..e5d896fb2 100644 --- a/livesync/indico_livesync/base.py +++ b/livesync/indico_livesync/base.py @@ -16,12 +16,13 @@ from indico.modules.events.contributions.models.subcontributions import SubContribution from indico.modules.events.models.events import Event from indico.modules.events.notes.models.notes import EventNote +from indico.modules.events.registration.models.registrations import Registration from indico.util.date_time import now_utc from indico.util.decorators import classproperty from indico_livesync.forms import AgentForm from indico_livesync.initial import (apply_acl_entry_strategy, query_attachments, query_contributions, query_events, - query_notes, query_subcontributions) + query_notes, query_registrations, query_subcontributions) from indico_livesync.models.queue import EntryType, LiveSyncQueueEntry from indico_livesync.plugin import LiveSyncPlugin @@ -143,6 +144,7 @@ def get_initial_query(self, model_cls, force): """ fn = { Event: query_events, + Registration: query_registrations, Contribution: query_contributions, SubContribution: query_subcontributions, Attachment: query_attachments, @@ -164,6 +166,7 @@ def get_data_query(self, model_cls, ids): """ fn = { Event: query_events, + Registration: query_registrations, Contribution: query_contributions, SubContribution: query_subcontributions, Attachment: query_attachments, @@ -184,6 +187,7 @@ def run_initial_export(self, batch_size, force=False, verbose=False): self._precache_categories() events = self.get_initial_query(Event, force) + registrations = self.get_initial_query(Registration, force) contributions = self.get_initial_query(Contribution, force) subcontributions = self.get_initial_query(SubContribution, force) attachments = self.get_initial_query(Attachment, force) @@ -193,6 +197,10 @@ def run_initial_export(self, batch_size, force=False, verbose=False): if not uploader.run_initial(events.yield_per(batch_size), events.count()): print('Initial export of events failed') return False + print('Exporting registrations') + if not uploader.run_initial(registrations.yield_per(batch_size), registrations.count()): + print('Initial export of registrations failed') + return False print('Exporting contributions') if not uploader.run_initial(contributions.yield_per(batch_size), contributions.count()): print('Initial export of contributions failed') diff --git a/livesync/indico_livesync/handler.py b/livesync/indico_livesync/handler.py index 3c71f1790..550892980 100644 --- a/livesync/indico_livesync/handler.py +++ b/livesync/indico_livesync/handler.py @@ -20,6 +20,7 @@ from indico.modules.events.contributions.models.contributions import Contribution from indico.modules.events.contributions.models.subcontributions import SubContribution from indico.modules.events.notes.models.notes import EventNote +from indico.modules.events.registration.models.registrations import Registration from indico.modules.events.sessions import Session from indico.modules.events.sessions.models.blocks import SessionBlock from indico.modules.events.timetable.models.entries import TimetableEntryType @@ -39,14 +40,18 @@ def connect_signals(plugin): plugin.connect(signals.event.restored, _restored) plugin.connect(signals.event.contribution_created, _created) plugin.connect(signals.event.subcontribution_created, _created) + plugin.connect(signals.event.registration_created, _created) # deleted plugin.connect(signals.event.deleted, _deleted) plugin.connect(signals.event.contribution_deleted, _deleted) plugin.connect(signals.event.subcontribution_deleted, _deleted) + plugin.connect(signals.event.registration_deleted, _deleted) # updated plugin.connect(signals.event.updated, _updated) plugin.connect(signals.event.contribution_updated, _updated) plugin.connect(signals.event.subcontribution_updated, _updated) + plugin.connect(signals.event.registration_updated, _updated) + plugin.connect(signals.event.registration_form_edited, _updated) # event times plugin.connect(signals.event.times_changed, _event_times_changed, sender=Event) plugin.connect(signals.event.times_changed, _event_times_changed, sender=Contribution) @@ -113,7 +118,7 @@ def _moved(obj, old_parent, **kwargs): def _created(obj, **kwargs): - if not isinstance(obj, (Event, EventNote, Attachment, Contribution, SubContribution)): + if not isinstance(obj, (Event, EventNote, Attachment, Contribution, SubContribution, Registration)): raise TypeError(f'Unexpected object: {type(obj).__name__}') _register_change(obj, ChangeType.created) diff --git a/livesync/indico_livesync/initial.py b/livesync/indico_livesync/initial.py index cc0e687f2..c0ac6635d 100644 --- a/livesync/indico_livesync/initial.py +++ b/livesync/indico_livesync/initial.py @@ -17,9 +17,11 @@ from indico.modules.events.contributions.models.subcontributions import SubContribution from indico.modules.events.models.principals import EventPrincipal from indico.modules.events.notes.models.notes import EventNote, EventNoteRevision +from indico.modules.events.registration.models.registrations import Registration from indico.modules.events.sessions import Session from indico.modules.events.sessions.models.blocks import SessionBlock from indico.modules.events.sessions.models.principals import SessionPrincipal +from indico.modules.users.models.users import User from indico_livesync.util import get_excluded_categories @@ -64,6 +66,27 @@ def query_events(ids=None): ) +def query_registrations(ids=None): + event_strategy = contains_eager(Registration.event) + apply_acl_entry_strategy(event_strategy.selectinload(Event.acl_entries), EventPrincipal) + + if ids is None: + export_filter = ~Registration.is_deleted & ~Event.is_deleted & _get_excluded_category_filter() + else: + export_filter = Registration.id.in_(ids) + + return ( + Registration.query + .join(Event) + .join(User) + .filter(export_filter) + .options( + event_strategy, + ) + .order_by(Registration.id) + ) + + def query_contributions(ids=None): event_strategy = contains_eager(Contribution.event) event_strategy.joinedload(Event.own_venue) diff --git a/livesync/indico_livesync/migrations/20230306_1520_86fd11a6cab4_add_registrations_to_livesync.py b/livesync/indico_livesync/migrations/20230306_1520_86fd11a6cab4_add_registrations_to_livesync.py new file mode 100644 index 000000000..1c4f3f96f --- /dev/null +++ b/livesync/indico_livesync/migrations/20230306_1520_86fd11a6cab4_add_registrations_to_livesync.py @@ -0,0 +1,92 @@ +"""add registrations to livesync + +Revision ID: 86fd11a6cab4 +Revises: ff1323696f67 +Create Date: 2023-03-06 15:20:02.568517 +""" + +from alembic import op + + +# revision identifiers, used by Alembic. +revision = '86fd11a6cab4' +down_revision = 'ff1323696f67' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute(''' + alter table plugin_livesync.queues add column registration_id integer; + create index ix_queues_registration_id on plugin_livesync.queues using btree (registration_id); + alter table plugin_livesync.queues add constraint ck_queues_valid_registration_entry check (type <> 8 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND registration_id IS NOT NULL); + alter table plugin_livesync.queues add constraint fk_queues_registration_id_event_registrations foreign key (registration_id) references event_registration.registrations(id); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_enum_change; + alter table plugin_livesync.queues add constraint ck_queues_valid_enum_change CHECK (change = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9])); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_enum_type; + alter table plugin_livesync.queues add constraint ck_queues_valid_enum_type CHECK (type = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8])); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_attachment_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_attachment_entry CHECK (type <> 7 OR category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND registration_id IS NULL AND attachment_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_category_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_category_entry CHECK (type <> 1 OR attachment_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND registration_id IS NULL AND category_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_contribution_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_contribution_entry CHECK (type <> 3 OR attachment_id IS NULL AND category_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND registration_id IS NULL AND contribution_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_event_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_event_entry CHECK (type <> 2 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND registration_id IS NULL AND event_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_note_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_note_entry CHECK (type <> 6 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND registration_id IS NULL AND note_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_session_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_session_entry CHECK (type <> 5 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND subcontribution_id IS NULL AND registration_id IS NULL AND session_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_subcontribution_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_subcontribution_entry CHECK (type <> 4 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND registration_id IS NULL AND subcontribution_id IS NOT NULL); + + ''') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute(''' + alter table plugin_livesync.queues drop column registration_id; + alter table plugin_livesync.queues drop constraint ck_queues_valid_registration_entry; + + alter table plugin_livesync.queues drop constraint ck_queues_valid_enum_change; + alter table plugin_livesync.queues add constraint ck_queues_valid_enum_change CHECK (change = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9])); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_enum_type; + alter table plugin_livesync.queues add constraint ck_queues_valid_enum_type CHECK (type = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7])); + + + alter table plugin_livesync.queues drop constraint ck_queues_valid_attachment_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_attachment_entry CHECK (type <> 7 OR category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND attachment_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_category_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_category_entry CHECK (type <> 1 OR attachment_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND category_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_contribution_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_contribution_entry CHECK (type <> 3 OR attachment_id IS NULL AND category_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND contribution_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_event_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_event_entry CHECK (type <> 2 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND event_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_note_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_note_entry CHECK (type <> 6 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND session_id IS NULL AND subcontribution_id IS NULL AND note_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_session_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_session_entry CHECK (type <> 5 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND subcontribution_id IS NULL AND session_id IS NOT NULL); + + alter table plugin_livesync.queues drop constraint ck_queues_valid_subcontribution_entry; + alter table plugin_livesync.queues add constraint ck_queues_valid_subcontribution_entry CHECK (type <> 4 OR attachment_id IS NULL AND category_id IS NULL AND contribution_id IS NULL AND event_id IS NULL AND note_id IS NULL AND session_id IS NULL AND subcontribution_id IS NOT NULL); + + ''') + # ### end Alembic commands ### diff --git a/livesync/indico_livesync/models/queue.py b/livesync/indico_livesync/models/queue.py index 70461c214..5f86f66e4 100644 --- a/livesync/indico_livesync/models/queue.py +++ b/livesync/indico_livesync/models/queue.py @@ -38,6 +38,7 @@ class EntryType(int, IndicoEnum): session = 5 note = 6 attachment = 7 + registration = 8 _column_for_types = { @@ -48,6 +49,7 @@ class EntryType(int, IndicoEnum): EntryType.session: 'session_id', EntryType.note: 'note_id', EntryType.attachment: 'attachment_id', + EntryType.registration: 'registration_id', } @@ -131,6 +133,14 @@ class LiveSyncQueueEntry(db.Model): nullable=True ) + #: ID of the changed registration + registration_id = db.Column( + db.Integer, + db.ForeignKey('event_registration.registrations.id'), + index=True, + nullable=True + ) + #: ID of the changed session session_id = db.Column( 'session_id', @@ -213,6 +223,16 @@ class LiveSyncQueueEntry(db.Model): ) ) + registration = db.relationship( + 'Registration', + lazy=True, + backref=db.backref( + 'livesync_queue_entries', + cascade='all, delete-orphan', + lazy='dynamic' + ) + ) + subcontribution = db.relationship( 'SubContribution', lazy=False, @@ -260,6 +280,8 @@ def object(self): return self.note elif self.type == EntryType.attachment: return self.attachment + elif self.type == EntryType.registration: + return self.registration def __repr__(self): return format_repr(self, 'id', 'agent_id', 'change', 'type', diff --git a/livesync/indico_livesync/util.py b/livesync/indico_livesync/util.py index d254b708d..f53625b23 100644 --- a/livesync/indico_livesync/util.py +++ b/livesync/indico_livesync/util.py @@ -16,6 +16,7 @@ from indico.modules.events.contributions.models.contributions import Contribution from indico.modules.events.contributions.models.subcontributions import SubContribution from indico.modules.events.notes.models.notes import EventNote +from indico.modules.events.registration.models.registrations import Registration from indico.modules.events.sessions.models.sessions import Session from indico.util.caching import memoize_request from indico.util.date_time import now_utc @@ -38,6 +39,8 @@ def obj_ref(obj): ref = {'type': EntryType.note, 'note_id': obj.id} elif isinstance(obj, Attachment): ref = {'type': EntryType.attachment, 'attachment_id': obj.id} + elif isinstance(obj, Registration): + ref = {'type': EntryType.registration, 'registration_id': obj.id} else: raise ValueError(f'Unexpected object: {obj.__class__.__name__}') return ImmutableDict(ref) @@ -61,6 +64,8 @@ def obj_deref(ref): return EventNote.get_or_404(ref['note_id']) elif ref['type'] == EntryType.attachment: return Attachment.get_or_404(ref['attachment_id']) + elif ref['type'] == EntryType.registration: + return Attachment.get_or_404(ref['registration_id']) else: raise ValueError('Unexpected object type: {}'.format(ref['type']))