def get_upcoming_events(): """Get the global list of upcoming events""" from indico.modules.events import Event data = upcoming_events_settings.get_all() if not data['max_entries'] or not data['entries']: return tz = timezone(config.DEFAULT_TIMEZONE) now = now_utc(False).astimezone(tz) base_query = (Event.query .filter(Event.effective_protection_mode == ProtectionMode.public, ~Event.is_deleted, Event.end_dt.astimezone(tz) > now) .options(load_only('id', 'title', 'start_dt', 'end_dt'))) queries = [] predicates = {'category': lambda id_: Event.category_id == id_, 'category_tree': lambda id_: Event.category_chain_overlaps(id_) & Event.is_visible_in(id_), 'event': lambda id_: Event.id == id_} for entry in data['entries']: delta = timedelta(days=entry['days']) query = (base_query .filter(predicates[entry['type']](entry['id'])) .filter(db.cast(Event.start_dt.astimezone(tz), db.Date) > (now - delta).date()) .with_entities(Event, db.literal(entry['weight']).label('weight'))) queries.append(query) query = (queries[0].union(*queries[1:]) .order_by(db.desc('weight'), Event.start_dt, Event.title) .limit(data['max_entries'])) for row in query: event = row[0] # we cache the result of the function and is_deleted is used in the repr # and having a broken repr on the cached objects would be ugly set_committed_value(event, 'is_deleted', False) yield event
def get_events_with_abstract_reviewer_convener(user, dt=None): """ Return a dict of event ids and the abstract reviewing related roles the user has in that event. :param user: A `User` :param dt: Only include events taking place on/after that date """ data = defaultdict(set) # global reviewer/convener mapping = {'global_abstract_reviewer_for_events': 'abstract_reviewer', 'global_convener_for_events': 'track_convener'} for rel, role in mapping.iteritems(): query = (Event.query.with_parent(user, rel) .filter(Event.ends_after(dt), ~Event.is_deleted) .options(load_only('id'))) for event in query: data[event.id].add(role) # track reviewer/convener mapping = {'abstract_reviewer_for_tracks': 'abstract_reviewer', 'convener_for_tracks': 'track_convener'} for rel, role in mapping.iteritems(): query = (Track.query.with_parent(user, rel) .join(Track.event_new) .filter(Event.ends_after(dt), ~Event.is_deleted) .options(load_only('event_id'))) for track in query: data[track.event_id].add(role) return data
def event(self, idlist): query = (Event.find(Event.id.in_(idlist), ~Event.is_deleted, Event.happens_between(self._fromDT, self._toDT)) .options(*self._get_query_options(self._detail_level))) query = self._update_query(query) return self.serialize_events(x for x in query if self._filter_event(x) and x.can_access(self.user))
def get_events_with_abstract_persons(user, dt=None): """ Return a dict of event ids and the abstract submission related roles the user has in that event. :param user: A `User` :param dt: Only include events taking place on/after that date """ data = defaultdict(set) bad_states = {AbstractState.withdrawn, AbstractState.rejected} # submitter query = (Abstract.query .filter(~Event.is_deleted, ~Abstract.is_deleted, ~Abstract.state.in_(bad_states), Event.ends_after(dt), Abstract.submitter == user) .join(Abstract.event_new) .options(load_only('event_id'))) for abstract in query: data[abstract.event_id].add('abstract_submitter') # person abstract_criterion = db.and_(~Abstract.state.in_(bad_states), ~Abstract.is_deleted) query = (user.event_persons .filter(~Event.is_deleted, Event.ends_after(dt), EventPerson.abstract_links.any(AbstractPersonLink.abstract.has(abstract_criterion))) .join(EventPerson.event_new) .options(load_only('event_id'))) for person in query: data[person.event_id].add('abstract_person') return data
def category(self, idlist): query = (Event.query .filter(~Event.is_deleted, Event.category_chain_overlaps(map(int, idlist)), Event.happens_between(self._fromDT, self._toDT)) .options(*self._get_query_options(self._detail_level))) query = self._update_query(query) return self.serialize_events(x for x in query if self._filter_event(x) and x.can_access(self.user))
def category_events(self, catIds): from indico.modules.events import Event query = (Event.query .filter(~Event.is_deleted, Event.category_chain_overlaps(map(int, catIds)), Event.happens_between(self._fromDT, self._toDT)) .options(joinedload('category').load_only('id', 'title'))) return self._process(x.as_legacy for x in query)
def create_event(category, event_type, data, add_creator_as_manager=True, features=None): """Create a new event. :param category: The category in which to create the event :param event_type: An `EventType` value :param data: A dict containing data used to populate the event :param add_creator_as_manager: Whether the creator (current user) should be added as a manager :param features: A list of features that will be enabled for the event. If set, only those features will be used and the default feature set for the event type will be ignored. """ event = Event(category=category, type_=event_type) data.setdefault('creator', session.user) theme = data.pop('theme', None) person_link_data = data.pop('person_link_data', {}) event.populate_from_dict(data) db.session.flush() event.person_link_data = person_link_data if theme is not None: layout_settings.set(event, 'timetable_theme', theme) if add_creator_as_manager: with event.logging_disabled: event.update_principal(event.creator, full_access=True) if features is not None: features_event_settings.set(event, 'enabled', features) db.session.flush() signals.event.created.send(event) logger.info('Event %r created in %r by %r ', event, category, session.user) event.log(EventLogRealm.event, EventLogKind.positive, 'Event', 'Event created', session.user) db.session.flush() return event
def serialize_category_ical(category, user, event_filter): """Export the events in a category to iCal :param category: The category to export :param user: The user who needs to be able to access the events :param event_filter: A SQLalchemy criterion to restrict which events will be returned. Usually something involving the start/end date of the event. """ own_room_strategy = joinedload('own_room') own_room_strategy.load_only('building', 'floor', 'number', 'name') own_room_strategy.lazyload('owner') own_venue_strategy = joinedload('own_venue').load_only('name') query = (Event.query .filter(Event.category_chain_overlaps(category.id), ~Event.is_deleted, event_filter) .options(load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'description', 'own_venue_name', 'own_room_name', 'protection_mode', 'access_key'), subqueryload('acl_entries'), joinedload('person_links'), own_room_strategy, own_venue_strategy) .order_by(Event.start_dt)) events = [e for e in query if e.can_access(user)] cal = ical.Calendar() cal.add('version', '2.0') cal.add('prodid', '-//CERN//INDICO//EN') now = now_utc(False) for event in events: url = url_for('event.conferenceDisplay', confId=event.id, _external=True) location = ('{} ({})'.format(event.room_name, event.venue_name) if event.venue_name and event.room_name else (event.venue_name or event.room_name)) cal_event = ical.Event() cal_event.add('uid', u'indico-event-{}@cern.ch'.format(event.id)) cal_event.add('dtstamp', now) cal_event.add('dtstart', event.start_dt) cal_event.add('dtend', event.end_dt) cal_event.add('url', url) cal_event.add('summary', event.title) cal_event.add('location', location) description = [] if event.person_links: speakers = [u'{} ({})'.format(x.full_name, x.affiliation) if x.affiliation else x.full_name for x in event.person_links] description.append(u'Speakers: {}'.format(u', '.join(speakers))) if event.description: desc_text = unicode(event.description) or u'<p/>' # get rid of RichMarkup try: description.append(unicode(html.fromstring(desc_text).text_content())) except ParserError: # this happens e.g. if desc_text contains only a html comment pass description.append(url) cal_event.add('description', u'\n'.join(description)) cal.add_component(cal_event) return BytesIO(cal.to_ical())
def category_extra(self, ids): if self._toDT is None: has_future_events = False else: query = Event.find(Event.category_id.in_(ids), ~Event.is_deleted, Event.start_dt > self._toDT) has_future_events = query.has_rows() return {"eventCategories": self._build_category_path_data(ids), "moreFutureEvents": has_future_events}
def _iterate_objs(query_string): query = (Event.query .filter(Event.title_matches(to_unicode(query_string)), ~Event.is_deleted) .options(undefer('effective_protection_mode'))) sort_dir = db.desc if self._descending else db.asc if self._orderBy == 'start': query = query.order_by(sort_dir(Event.start_dt)) elif self._orderBy == 'end': query = query.order_by(sort_dir(Event.end_dt)) elif self._orderBy == 'id': query = query.order_by(sort_dir(Event.id)) elif self._orderBy == 'title': query = query.order_by(sort_dir(db.func.lower(Event.title))) counter = 0 # Query the DB in chunks of 1000 records per query until the limit is satisfied for event in query.yield_per(1000): if event.can_access(self._user): counter += 1 # Start yielding only when the counter reaches the given offset if (self._offset is None) or (counter > self._offset): yield event # Stop querying the DB when the limit is satisfied if (self._limit is not None) and (counter == self._offset + self._limit): break
def _process(self): self.user.settings.set('suggest_categories', True) tz = session.tzinfo hours, minutes = timedelta_split(tz.utcoffset(datetime.now()))[:2] categories = get_related_categories(self.user) categories_events = [] if categories: category_ids = {c['categ'].id for c in categories.itervalues()} today = now_utc(False).astimezone(tz).date() query = (Event.query .filter(~Event.is_deleted, Event.category_chain_overlaps(category_ids), Event.start_dt.astimezone(session.tzinfo) >= today) .options(joinedload('category').load_only('id', 'title'), joinedload('series'), subqueryload('acl_entries'), load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'access_key', 'protection_mode', 'series_id', 'series_pos', 'series_count')) .order_by(Event.start_dt, Event.id)) categories_events = get_n_matching(query, 10, lambda x: x.can_access(self.user)) from_dt = now_utc(False) - relativedelta(weeks=1, hour=0, minute=0, second=0) linked_events = [(event, {'management': bool(roles & self.management_roles), 'reviewing': bool(roles & self.reviewer_roles), 'attendance': bool(roles & self.attendance_roles)}) for event, roles in get_linked_events(self.user, from_dt, 10).iteritems()] return WPUser.render_template('dashboard.html', 'dashboard', offset='{:+03d}:{:02d}'.format(hours, minutes), user=self.user, categories=categories, categories_events=categories_events, suggested_categories=get_suggested_categories(self.user), linked_events=linked_events)
def deserialize(self): if not self.force and self.data['indico_version'] != indico.__version__: click.secho('Version mismatch: trying to import event exported with {} to version {}' .format(self.data['indico_version'], indico.__version__), fg='red') return None self._load_users(self.data) for tablename, tabledata in self.data['objects']: self._deserialize_object(db.metadata.tables[tablename], tabledata) if self.deferred_idrefs: # Any reference to an ID that was exported need to be replaced # with an actual ID at some point - either immediately (if the # referenced row was already imported) or later (usually in case # of circular dependencies where one of the IDs is not available # when the row is inserted). click.secho('BUG: Not all deferred idrefs have been consumed', fg='red') for uuid, values in self.deferred_idrefs.iteritems(): click.secho('{}:'.format(uuid), fg='yellow', bold=True) for table, col, pk_value in values: click.secho(' - {}.{} ({})'.format(table.fullname, col, pk_value), fg='yellow') raise Exception('Not all deferred idrefs have been consumed') event = Event.get(self.event_id) event.log(EventLogRealm.event, EventLogKind.other, 'Event', 'Event imported from another Indico instance') self._associate_users_by_email(event) db.session.flush() return event
def _getParams(self): super(NoteExportHook, self)._getParams() event = self._obj = Event.get(self._pathParams['event_id'], is_deleted=False) if event is None: raise HTTPAPIError('No such event', 404) session_id = self._pathParams.get('session_id') if session_id: self._obj = Session.query.with_parent(event).filter_by(id=session_id).first() if self._obj is None: raise HTTPAPIError("No such session", 404) contribution_id = self._pathParams.get('contribution_id') if contribution_id: contribution = self._obj = (Contribution.query.with_parent(event) .filter_by(id=contribution_id, is_deleted=False) .first()) if contribution is None: raise HTTPAPIError("No such contribution", 404) subcontribution_id = self._pathParams.get('subcontribution_id') if subcontribution_id: self._obj = SubContribution.query.with_parent(contribution).filter_by(id=subcontribution_id, is_deleted=False).first() if self._obj is None: raise HTTPAPIError("No such subcontribution", 404) self._note = EventNote.get_for_linked_object(self._obj, preload_event=False) if self._note is None or self._note.is_deleted: raise HTTPAPIError("No such note", 404)
def event_or_shorturl(confId, shorturl_namespace=False, force_overview=False): func = None event_ = Event.get(int(confId)) if confId.isdigit() else None if event_ and event_.is_deleted: raise NotFound(_('This event has been deleted.')) elif event_: # For obvious reasons an event id always comes first. # If it's used within the short url namespace we redirect to the event namespace, otherwise # we call the RH to display the event if shorturl_namespace: func = lambda: redirect(event_.url) else: request.view_args['confId'] = int(request.view_args['confId']) func = lambda: RHDisplayEvent().process() else: shorturl_event = (Event.query .filter(db.func.lower(Event.url_shortcut) == confId.lower(), ~Event.is_deleted) .one_or_none()) if (shorturl_namespace or config.ROUTE_OLD_URLS) and shorturl_event: if shorturl_namespace: # Correct namespace => redirect to the event func = lambda: redirect(shorturl_event.url) else: # Old event namespace => 301-redirect to the new shorturl first to get Google etc. to update it func = lambda: redirect(shorturl_event.short_url, 301) elif is_legacy_id(confId): mapping = LegacyEventMapping.find_first(legacy_event_id=confId) if mapping is not None: url = url_for('events.display', confId=mapping.event_id) func = lambda: redirect(url, 301) if func is None: raise NotFound(_('An event with this ID/shortcut does not exist.')) return func()
def test_deleted_relationships(db, dummy_event_new): event = dummy_event_new assert not event.contributions assert not event.sessions s = Session(event_new=event, title='s') sd = Session(event_new=event, title='sd', is_deleted=True) c = Contribution(event_new=event, title='c', session=sd, duration=timedelta(minutes=30)) cd = Contribution(event_new=event, title='cd', session=sd, duration=timedelta(minutes=30), is_deleted=True) sc = SubContribution(contribution=c, title='sc', duration=timedelta(minutes=10)) scd = SubContribution(contribution=c, title='scd', duration=timedelta(minutes=10), is_deleted=True) db.session.flush() db.session.expire_all() # reload all the objects from the db event = Event.get(event.id) s = Session.get(s.id) sd = Session.get(sd.id) c = Contribution.get(c.id) cd = Contribution.get(cd.id) sc = SubContribution.get(sc.id) scd = SubContribution.get(scd.id) # deleted items should not be in the lists assert event.sessions == [s] assert event.contributions == [c] assert sd.contributions == [c] assert c.subcontributions == [sc] # the other direction should work fine even in case of deletion assert s.event_new == event assert sd.event_new == event assert c.event_new == event assert cd.event_new == event assert sc.contribution == c assert scd.contribution == c
def get_events_with_linked_sessions(user, dt=None): """Returns a dict with keys representing event_id and the values containing data about the user rights for sessions within the event :param user: A `User` :param dt: Only include events taking place on/after that date """ query = (user.in_session_acls .options(load_only('session_id', 'roles', 'full_access', 'read_access')) .options(noload('*')) .options(contains_eager(SessionPrincipal.session).load_only('event_id')) .join(Session) .join(Event, Event.id == Session.event_id) .filter(~Session.is_deleted, ~Event.is_deleted, Event.ends_after(dt))) data = defaultdict(set) for principal in query: roles = data[principal.session.event_id] if 'coordinate' in principal.roles: roles.add('session_coordinator') if 'submit' in principal.roles: roles.add('session_submission') if principal.full_access: roles.add('session_manager') if principal.read_access: roles.add('session_access') return data
def category_events(self, catIds): from indico.modules.events import Event query = (Event.query .filter(~Event.is_deleted, Event.category_chain.overlap(map(int, catIds)), Event.happens_between(self._fromDT, self._toDT))) return self._process(x.as_legacy for x in query)
def get_attachment_count(category_id=None): """Get the number of attachments in events in a category. :param category_id: The category ID to get statistics for. Attachments from subcategories are also included. :return: The number of attachments """ category_filter = Event.category_chain_overlaps(category_id) if category_id else True subcontrib_contrib = db.aliased(Contribution) query = (db.session .query(db.func.count(Attachment.id)) .join(Attachment.folder) .join(AttachmentFolder.event) .outerjoin(AttachmentFolder.session) .outerjoin(AttachmentFolder.contribution) .outerjoin(AttachmentFolder.subcontribution) .outerjoin(subcontrib_contrib, subcontrib_contrib.id == SubContribution.contribution_id) .filter(AttachmentFolder.link_type != LinkType.category, ~Attachment.is_deleted, ~AttachmentFolder.is_deleted, ~Event.is_deleted, # we have exactly one of those or none if the attachment is on the event itself ~db.func.coalesce(Session.is_deleted, Contribution.is_deleted, SubContribution.is_deleted, False), # in case of a subcontribution we also need to check that the contrib is not deleted (subcontrib_contrib.is_deleted.is_(None) | ~subcontrib_contrib.is_deleted), category_filter)) return query.scalar()
def get_events_with_linked_sessions(user, from_dt=None, to_dt=None): """Returns a dict with keys representing event_id and the values containing data about the user rights for sessions within the event :param user: A `User` :param from_dt: The earliest event start time to look for :param to_dt: The latest event start time to look for """ query = (user.in_session_acls .options(load_only('session_id', 'roles', 'full_access', 'read_access')) .options(noload('*')) .options(contains_eager(SessionPrincipal.session).load_only('event_id')) .join(Session) .join(Event, Event.id == Session.event_id) .filter(~Session.is_deleted, ~Event.is_deleted, Event.starts_between(from_dt, to_dt))) data = defaultdict(set) for principal in query: roles = data[principal.session.event_id] if 'coordinate' in principal.roles: roles.add('session_coordinator') if 'submit' in principal.roles: roles.add('session_submission') if principal.full_access: roles.add('session_manager') if principal.read_access: roles.add('session_access') return data
def serialize_category_atom(category, url, user, event_filter): """Export the events in a category to Atom :param category: The category to export :param url: The URL of the feed :param user: The user who needs to be able to access the events :param event_filter: A SQLalchemy criterion to restrict which events will be returned. Usually something involving the start/end date of the event. """ query = (Event.query .filter(Event.category_chain_overlaps(category.id), ~Event.is_deleted, event_filter) .options(load_only('id', 'category_id', 'start_dt', 'title', 'description', 'protection_mode', 'access_key'), subqueryload('acl_entries')) .order_by(Event.start_dt)) events = [e for e in query if e.can_access(user)] feed = AtomFeed(feed_url=url, title='Indico Feed [{}]'.format(category.title)) for event in events: feed.add(title=event.title, summary=unicode(event.description), # get rid of RichMarkup url=event.external_url, updated=event.start_dt) return BytesIO(feed.to_string().encode('utf-8'))
def create_event(category, event_type, data, add_creator_as_manager=True, features=None): from indico.modules.rb_new.operations.bookings import create_booking_for_event """Create a new event. :param category: The category in which to create the event :param event_type: An `EventType` value :param data: A dict containing data used to populate the event :param add_creator_as_manager: Whether the creator (current user) should be added as a manager :param features: A list of features that will be enabled for the event. If set, only those features will be used and the default feature set for the event type will be ignored. """ event = Event(category=category, type_=event_type) data.setdefault('creator', session.user) theme = data.pop('theme', None) create_booking = data.pop('create_booking', False) person_link_data = data.pop('person_link_data', {}) event.populate_from_dict(data) db.session.flush() event.person_link_data = person_link_data if theme is not None: layout_settings.set(event, 'timetable_theme', theme) if add_creator_as_manager: with event.logging_disabled: event.update_principal(event.creator, full_access=True) if features is not None: features_event_settings.set(event, 'enabled', features) db.session.flush() signals.event.created.send(event) logger.info('Event %r created in %r by %r ', event, category, session.user) event.log(EventLogRealm.event, EventLogKind.positive, 'Event', 'Event created', session.user) db.session.flush() if create_booking: room_id = data['location_data'].pop('room_id', None) if room_id: booking = create_booking_for_event(room_id, event) if booking: logger.info('Booking %r created for event %r', booking, event) log_data = {'Room': booking.room.full_name, 'Date': booking.start_dt.strftime('%d/%m/%Y'), 'Times': '%s - %s' % (booking.start_dt.strftime('%H:%M'), booking.end_dt.strftime('%H:%M'))} event.log(EventLogRealm.event, EventLogKind.positive, 'Event', 'Room booked for the event', session.user, data=log_data) db.session.flush() return event
def _iterate_objs(query_string): query = Event.find(Event.title_matches(to_unicode(query_string)), ~Event.is_deleted) if self._orderBy == 'start': query = query.order_by(Event.start_dt) elif self._orderBy == 'id': query = query.order_by(Event.id) counter = 0 # Query the DB in chunks of 1000 records per query until the limit is satisfied for event in query.yield_per(1000): if event.can_access(self._aw.getUser().user if self._aw.getUser() else None): counter += 1 # Start yielding only when the counter reaches the given offset if (self._offset is None) or (counter > self._offset): yield event # Stop querying the DB when the limit is satisfied if (self._limit is not None) and (counter == self._offset + self._limit): break
def _process_args(self): if 'confId' in request.view_args: self.obj = Event.get_one(request.view_args['confId'], is_deleted=False) self.obj_type = 'event' elif 'category_id' in request.view_args: self.obj = Category.get_one(request.view_args['category_id'], is_deleted=False) self.obj_type = 'category' if not self.obj.is_root else None else: self.obj = Category.get_root() self.obj_type = None
def _getParams(self): super(AgreementExportHook, self)._getParams() type_ = self._pathParams['agreement_type'] try: self._definition = get_agreement_definitions()[type_] except KeyError: raise HTTPAPIError('No such agreement type', 404) self.event = Event.get(self._pathParams['event_id'], is_deleted=False) if self.event is None: raise HTTPAPIError('No such event', 404)
def get_events_created_by(user, from_dt=None, to_dt=None): """Gets the IDs of events created by the user :param user: A `User` :param from_dt: The earliest event start time to look for :param to_dt: The latest event start time to look for :return: A set of event ids """ query = user.created_events.filter(~Event.is_deleted, Event.starts_between(from_dt, to_dt)) return {event.id for event in query}
def category(self, idlist, format): try: idlist = map(int, idlist) except ValueError: raise HTTPAPIError('Category IDs must be numeric', 400) if format == 'ics': buf = serialize_categories_ical(idlist, self.user, event_filter=Event.happens_between(self._fromDT, self._toDT), event_filter_fn=self._filter_event, update_query=self._update_query) return send_file('events.ics', buf, 'text/calendar') else: query = (Event.query .filter(~Event.is_deleted, Event.category_chain_overlaps(idlist), Event.happens_between(self._fromDT, self._toDT)) .options(*self._get_query_options(self._detail_level))) query = self._update_query(query) return self.serialize_events(x for x in query if self._filter_event(x) and x.can_access(self.user))
def category_extra(self, ids): if self._toDT is None: has_future_events = False else: query = Event.find(Event.category_id.in_(ids), ~Event.is_deleted, Event.start_dt > self._toDT) has_future_events = db.session.query(query.exists()).one()[0] return { 'eventCategories': self._build_category_path_data(ids), 'moreFutureEvents': has_future_events }
def get_events_created_by(user, dt=None): """Gets the IDs of events created by the user :param user: A `User` :param dt: Only include events taking place on/after that date :return: A set of event ids """ query = (user.created_events .filter(~Event.is_deleted, Event.ends_after(dt)) .options(load_only('id'))) return {event.id for event in query}
def restore(event_id): """Restores a deleted event.""" event = Event.get(event_id) if event is None: click.secho('This event does not exist', fg='red') sys.exit(1) elif not event.is_deleted: click.secho('This event is not deleted', fg='yellow') sys.exit(1) event.is_deleted = False db.session.commit() click.secho('Event undeleted: "{}"'.format(event.title), fg='green')
def validate_entries(self, field): if field.errors: return for entry in field.data: if entry['days'] < 0: raise ValidationError(_("'Days' must be a positive integer")) if entry['type'] not in {'category', 'event'}: raise ValidationError(_('Invalid type')) if entry['type'] == 'category' and not Category.get(entry['id'], is_deleted=False): raise ValidationError(_('Invalid category: {}').format(entry['id'])) if entry['type'] == 'event' and not Event.get(entry['id'], is_deleted=False): raise ValidationError(_('Invalid event: {}').format(entry['id']))
def serialize_categories_ical(category_ids, user, event_filter=True, event_filter_fn=None, update_query=None): """Export the events in a category to iCal. :param category_ids: Category IDs to export :param user: The user who needs to be able to access the events :param event_filter: A SQLalchemy criterion to restrict which events will be returned. Usually something involving the start/end date of the event. :param event_filter_fn: A callable that determines which events to include (after querying) :param update_query: A callable that can update the query used to retrieve the events. Must return the updated query object. """ own_room_strategy = joinedload('own_room') own_room_strategy.load_only('building', 'floor', 'number', 'verbose_name') own_room_strategy.lazyload('owner') own_venue_strategy = joinedload('own_venue').load_only('name') query = (Event.query .filter(Event.category_chain_overlaps(category_ids), ~Event.is_deleted, event_filter) .options(load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'description', 'own_venue_name', 'own_room_name', 'protection_mode', 'access_key'), subqueryload('acl_entries'), joinedload('person_links'), own_room_strategy, own_venue_strategy) .order_by(Event.start_dt)) if update_query: query = update_query(query) it = iter(query) if event_filter_fn: it = filter(event_filter_fn, it) events = list(it) # make sure the parent categories are in sqlalchemy's identity cache. # this avoids query spam from `protection_parent` lookups _parent_categs = (Category._get_chain_query(Category.id.in_({e.category_id for e in events})) # noqa: F841 .options(load_only('id', 'parent_id', 'protection_mode'), joinedload('acl_entries')) .all()) return BytesIO(events_to_ical(events, user))
def _create_event(id_=None, legacy=False, **kwargs): conf = MockConference() # we specify `acl_entries` so SA doesn't load it when accessing it for # the first time, which would require no_autoflush blocks in some cases now = now_utc(exact=False) kwargs.setdefault('type_', EventType.meeting) kwargs.setdefault( 'title', u'dummy#{}'.format(id_) if id_ is not None else u'dummy') kwargs.setdefault('start_dt', now) kwargs.setdefault('end_dt', now + timedelta(hours=1)) kwargs.setdefault('timezone', 'UTC') kwargs.setdefault('category', dummy_category) conf.as_event = Event(id=id_, creator=dummy_user, acl_entries=set(), **kwargs) db.session.flush() conf.id = str(conf.as_event.id) ch.add(conf) _events.append(conf) return conf if legacy else conf.as_event
def _iterate_objs(query_string): query = (Event.query .filter(Event.title_matches(to_unicode(query_string)), ~Event.is_deleted) .options(undefer('effective_protection_mode'))) if self._orderBy == 'start': query = query.order_by(Event.start_dt) elif self._orderBy == 'id': query = query.order_by(Event.id) counter = 0 # Query the DB in chunks of 1000 records per query until the limit is satisfied for event in query.yield_per(1000): if event.can_access(self._aw.getUser().user if self._aw.getUser() else None): counter += 1 # Start yielding only when the counter reaches the given offset if (self._offset is None) or (counter > self._offset): yield event # Stop querying the DB when the limit is satisfied if (self._limit is not None) and (counter == self._offset + self._limit): break
def _send_email(with_event): from flask import session from indico.core.notifications import send_email, make_email from indico.modules.events import Event from indico.modules.users import User from indico.web.flask.templating import get_template_module tpl = get_template_module('users/emails/registration_request_accepted.txt', user=User.get(6)) kwargs = {} if with_event: kwargs = { 'event': Event.get(654658), 'module': 'Test', 'user': session.user } # send_email(make_email('m\[email protected]', template=tpl), **kwargs) # send_email(make_email('*****@*****.**', template=tpl), **kwargs) from indico.modules.events.registration.notifications import _notify_registration from indico.modules.events.registration.models.registrations import Registration _notify_registration(Registration.get(56757), 'registration_creation_to_registrant.html')
def _process(self): self.user.settings.set('suggest_categories', True) tz = timezone(DisplayTZ().getDisplayTZ()) hours, minutes = timedelta_split(tz.utcoffset(datetime.now()))[:2] categories = get_related_categories(self.user) categories_events = [] if categories: category_ids = {c['categ'].id for c in categories.itervalues()} today = now_utc(False).astimezone(session.tzinfo).date() query = (Event.query.filter( ~Event.is_deleted, Event.category_chain_overlaps(category_ids), Event.start_dt.astimezone(session.tzinfo) >= today).options( joinedload('category').load_only('id', 'title'), joinedload('series'), subqueryload('acl_entries'), load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'access_key', 'protection_mode', 'series_id', 'series_pos', 'series_count')).order_by( Event.start_dt, Event.id)) categories_events = get_n_matching( query, 10, lambda x: x.can_access(self.user)) from_dt = now_utc(False) - relativedelta( weeks=1, hour=0, minute=0, second=0) linked_events = [(event, { 'management': bool(roles & self.management_roles), 'reviewing': bool(roles & self.reviewer_roles), 'attendance': bool(roles & self.attendance_roles) }) for event, roles in get_linked_events( self.user, from_dt, 10).iteritems()] return WPUserDashboard.render_template( 'dashboard.html', 'dashboard', timezone=unicode(tz), offset='{:+03d}:{:02d}'.format(hours, minutes), user=self.user, categories=categories, categories_events=categories_events, suggested_categories=get_suggested_categories(self.user), linked_events=linked_events)
def get_object_from_args(args=None): """Retrieve an event object from request arguments. This utility is meant to be used in cases where the same controller can deal with objects attached to various parts of an event which use different URLs to indicate which object to use. :param args: The request arguments. If unspecified, ``request.view_args`` is used. :return: An ``(object_type, event, object)`` tuple. The event is always the :class:`Event` associated with the object. The object may be an `Event`, `Session`, `Contribution` or `SubContribution`. If the object does not exist, ``(object_type, None, None)`` is returned. """ if args is None: args = request.view_args object_type = args['object_type'] event = Event.get(args['event_id'], is_deleted=False) if event is None: obj = None elif object_type == 'event': obj = event elif object_type == 'session': obj = Session.query.with_parent(event).filter_by(id=args['session_id']).first() elif object_type == 'contribution': obj = Contribution.query.with_parent(event).filter_by(id=args['contrib_id']).first() elif object_type == 'subcontribution': obj = (SubContribution.query .filter(SubContribution.id == args['subcontrib_id'], ~SubContribution.is_deleted, SubContribution.contribution.has(event=event, id=args['contrib_id'], is_deleted=False)) .first()) else: raise ValueError(f'Unexpected object type: {object_type}') if obj is not None: return object_type, event, obj else: return object_type, None, None
def event_or_shorturl(confId, shorturl_namespace=False, force_overview=False): func = None event_ = Event.get(int(confId)) if confId.isdigit() else None if event_ and event_.is_deleted: raise NotFound(_('This event has been deleted.')) elif event_: # For obvious reasons an event id always comes first. # If it's used within the short url namespace we redirect to the event namespace, otherwise # we call the RH to display the event if shorturl_namespace: func = lambda: redirect(event_.url) else: params = request.args.to_dict() params['confId'] = confId request.view_args['confId'] = int(request.view_args['confId']) func = lambda: RHDisplayEvent().process(params) else: shorturl_event = (Event.query.filter( db.func.lower(Event.url_shortcut) == confId.lower(), ~Event.is_deleted).one_or_none()) if (shorturl_namespace or current_app.config['INDICO_COMPAT_ROUTES']) and shorturl_event: if shorturl_namespace: # Correct namespace => redirect to the event func = lambda: redirect(shorturl_event.url) else: # Old event namespace => 301-redirect to the new shorturl first to get Google etc. to update it func = lambda: redirect(shorturl_event.short_url, 301) elif is_legacy_id(confId): mapping = LegacyEventMapping.find_first(legacy_event_id=confId) if mapping is not None: url = url_for('events.display', confId=mapping.event_id) func = lambda: redirect(url, 301) if func is None: raise NotFound(_('An event with this ID/shortcut does not exist.')) return func()
def event_or_shorturl(event_id, shorturl_namespace=False, force_overview=False): event = Event.get(int(event_id)) if event_id.isdigit() and ( event_id[0] != '0' or event_id == '0') else None if event and event.is_deleted: raise NotFound(_('This event has been deleted.')) elif event: # For obvious reasons an event id always comes first. # If it's used within the short url namespace we redirect to the event namespace, otherwise # we call the RH to display the event if shorturl_namespace: return redirect(event.url) elif not request.path.endswith('/'): return redirect(event.url, 301) else: request.view_args['event_id'] = int(event_id) return RHDisplayEvent().process() else: shorturl_event = (Event.query.filter( db.func.lower(Event.url_shortcut) == event_id.lower(), ~Event.is_deleted).one_or_none()) if (shorturl_namespace or config.ROUTE_OLD_URLS) and shorturl_event: if shorturl_namespace: # Correct namespace => redirect to the event return redirect(shorturl_event.url) else: # Old event namespace => 301-redirect to the new shorturl first to get Google etc. to update it return redirect(shorturl_event.short_url, 301) elif is_legacy_id(event_id): mapping = LegacyEventMapping.query.filter_by( legacy_event_id=event_id).first() if mapping is not None: url = url_for('events.display', event_id=mapping.event_id) return redirect(url, 301) raise NotFound(_('An event with this ID/shortcut does not exist.'))
def _getParams(self): super(AttachmentsExportHook, self)._getParams() event = self._obj = Event.get(self._pathParams['event_id'], is_deleted=False) if event is None: raise HTTPAPIError('No such event', 404) session_id = self._pathParams.get('session_id') if session_id: self._obj = Session.query.with_parent(event).filter_by( id=session_id).first() if self._obj is None: raise HTTPAPIError("No such session", 404) contribution_id = self._pathParams.get('contribution_id') if contribution_id: contribution = self._obj = Contribution.query.with_parent( event).filter_by(id=contribution_id).first() if contribution is None: raise HTTPAPIError("No such contribution", 404) subcontribution_id = self._pathParams.get('subcontribution_id') if subcontribution_id: self._obj = SubContribution.query.with_parent( contribution).filter_by(id=subcontribution_id).first() if self._obj is None: raise HTTPAPIError("No such subcontribution", 404)
def migrate(self): all_series = self.get_event_series() all_series_ids = set(chain.from_iterable(all_series)) events = { e.id: e for e in Event.find(Event.id.in_(all_series_ids)).options( load_only('id', 'series_id')) } for series in all_series: series &= set(events) if len(series) < 2: self.print_warning('Skipping single-event series: {}'.format( sorted(series))) continue es = EventSeries(show_sequence_in_title=False) for id_ in series: events[id_].series = es if not self.quiet: self.print_success(repr(series)) (AttachmentFolder.query.filter( AttachmentFolder.title.op('~')('^part\d+$')).update( {AttachmentFolder.is_deleted: True}, synchronize_session=False)) db.session.commit()
def _process_args(self): RHUserBase._process_args(self) self.event = ( Event.get_or_404(request.view_args['event_id']) if 'event_id' in request.view_args else None )
def serialize_categories_ical(category_ids, user, event_filter=True, event_filter_fn=None, update_query=None): """Export the events in a category to iCal :param category_ids: Category IDs to export :param user: The user who needs to be able to access the events :param event_filter: A SQLalchemy criterion to restrict which events will be returned. Usually something involving the start/end date of the event. :param event_filter_fn: A callable that determines which events to include (after querying) :param update_query: A callable that can update the query used to retrieve the events. Must return the updated query object. """ own_room_strategy = joinedload('own_room') own_room_strategy.load_only('building', 'floor', 'number', 'name') own_room_strategy.lazyload('owner') own_venue_strategy = joinedload('own_venue').load_only('name') query = (Event.query.filter( Event.category_chain_overlaps(category_ids), ~Event.is_deleted, event_filter).options( load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'description', 'own_venue_name', 'own_room_name', 'protection_mode', 'access_key'), subqueryload('acl_entries'), joinedload('person_links'), own_room_strategy, own_venue_strategy).order_by(Event.start_dt)) if update_query: query = update_query(query) it = iter(query) if event_filter_fn: it = ifilter(event_filter_fn, it) events = list(it) # make sure the parent categories are in sqlalchemy's identity cache. # this avoids query spam from `protection_parent` lookups _parent_categs = (Category._get_chain_query( Category.id.in_({e.category_id for e in events})).options( load_only('id', 'parent_id', 'protection_mode'), joinedload('acl_entries')).all()) cal = ical.Calendar() cal.add('version', '2.0') cal.add('prodid', '-//CERN//INDICO//EN') now = now_utc(False) for event in events: if not event.can_access(user): continue location = ('{} ({})'.format(event.room_name, event.venue_name) if event.venue_name and event.room_name else (event.venue_name or event.room_name)) cal_event = ical.Event() cal_event.add( 'uid', u'indico-event-{}@{}'.format(event.id, url_parse(config.BASE_URL).host)) cal_event.add('dtstamp', now) cal_event.add('dtstart', event.start_dt) cal_event.add('dtend', event.end_dt) cal_event.add('url', event.external_url) cal_event.add('summary', event.title) cal_event.add('location', location) description = [] if event.person_links: speakers = [ u'{} ({})'.format(x.full_name, x.affiliation) if x.affiliation else x.full_name for x in event.person_links ] description.append(u'Speakers: {}'.format(u', '.join(speakers))) if event.description: desc_text = unicode( event.description) or u'<p/>' # get rid of RichMarkup try: description.append( unicode(html.fromstring(desc_text).text_content())) except ParserError: # this happens e.g. if desc_text contains only a html comment pass description.append(event.external_url) cal_event.add('description', u'\n'.join(description)) cal.add_component(cal_event) return BytesIO(cal.to_ical())
def _migrate_menu(self, event, container, parent=None, used=None): if used is None: used = set() for pos, item in enumerate(container._listLink, 1): data = { 'parent': parent, 'event_id': int(event.id), 'is_enabled': item._active, 'position': pos } item_type = item.__class__.__name__ if item_type == 'SystemLink': if item._name in REMOVED_MENU_NAMES: continue data['name'] = MENU_ENTRY_NAME_MAP[item._name] if not parent and data['name'] in NOT_TOP_LEVEL_NAMES: self.print_warning(cformat( '%{yellow}Skipping top-level menu entry {}').format( data['name']), event_id=event.id) continue elif data['name'] in used: self.print_error( cformat('%{red!}duplicate menu entry name {}; skipping' ).format(data['name']), event_id=event.id) continue used.add(data['name']) data['title'] = _sanitize_title(item._caption) if not data['title']: data['title'] = None self.print_warning(cformat( '%{yellow!}Menu entry {} has no title; using default'). format(data['name']), event_id=event.id) elif data['title'].lower() in DEFAULT_MENU_TITLES[ data['name']]: data['title'] = None if item._name == 'chatrooms': data['plugin'] = 'chat' data['type'] = MenuEntryType.plugin_link else: data['type'] = MenuEntryType.internal_link elif item_type == 'Spacer': data['type'] = MenuEntryType.separator elif item_type == 'ExternLink': data['type'] = MenuEntryType.user_link data['title'] = _sanitize_title(item._caption) data['link_url'] = item._URL.strip() if not data['link_url']: if getattr(item, '_listLink', None): self.print_warning(cformat( '%{yellow!}Link "{}" has no URL but children'). format(data['title']), event_id=event.id) else: self.print_warning(cformat( '%{yellow}Skipping link "{}" with no URL').format( data['title']), event_id=event.id) continue if not data['title']: if getattr(item, '_listLink', None): self.print_warning(cformat( '%{yellow!}Link has no title but children'), event_id=event.id) else: self.print_warning( cformat('%{yellow}Skipping link with no title'), event_id=event.id) continue elif item_type == 'PageLink': data['type'] = MenuEntryType.page data['title'] = _sanitize_title(item._caption) data['page'] = EventPage(event_id=event.id, html=item._page._content) data['page'].legacy_mapping = LegacyPageMapping( event_id=event.id, legacy_page_id=item._page._id) if item._page._isHome: Event.get(event.id).default_page = data['page'] else: self.print_error( 'Unexpected menu item type: {}'.format(item_type), event_id=event.id) continue entry = MenuEntry(**data) yield entry if getattr(item, '_listLink', None): # child entries if not parent: for sub_entry in self._migrate_menu( event, item, entry, used): yield sub_entry else: self.print_warning('Skipping children inside nested entry', event_id=event.id)
def search_notes(self, q, user, page, category_id, event_id, admin_override_enabled): contrib_event = db.aliased(Event) contrib_session = db.aliased(Session) subcontrib_contrib = db.aliased(Contribution) subcontrib_session = db.aliased(Session) subcontrib_event = db.aliased(Event) session_event = db.aliased(Event) note_strategy = load_only('id', 'link_type', 'event_id', 'linked_event_id', 'contribution_id', 'subcontribution_id', 'session_id', 'html') # event event_strategy = note_strategy.contains_eager(EventNote.linked_event) event_strategy.undefer(Event.effective_protection_mode) _apply_event_access_strategy(event_strategy) _apply_acl_entry_strategy( event_strategy.selectinload(Event.acl_entries), EventPrincipal) # contribution contrib_strategy = note_strategy.contains_eager(EventNote.contribution) _apply_contrib_access_strategy(contrib_strategy) _apply_acl_entry_strategy( contrib_strategy.selectinload(Contribution.acl_entries), ContributionPrincipal) contrib_event_strategy = contrib_strategy.contains_eager( Contribution.event.of_type(contrib_event)) _apply_event_access_strategy(contrib_event_strategy) _apply_acl_entry_strategy( contrib_event_strategy.selectinload(contrib_event.acl_entries), EventPrincipal) contrib_session_strategy = contrib_strategy.contains_eager( Contribution.session.of_type(contrib_session)) contrib_session_strategy.load_only('id', 'event_id', 'protection_mode') _apply_acl_entry_strategy( contrib_session_strategy.selectinload(contrib_session.acl_entries), SessionPrincipal) # subcontribution subcontrib_strategy = note_strategy.contains_eager( EventNote.subcontribution) subcontrib_contrib_strategy = subcontrib_strategy.contains_eager( SubContribution.contribution.of_type(subcontrib_contrib)) _apply_contrib_access_strategy(subcontrib_contrib_strategy) _apply_acl_entry_strategy( subcontrib_contrib_strategy.selectinload( subcontrib_contrib.acl_entries), ContributionPrincipal) subcontrib_event_strategy = subcontrib_contrib_strategy.contains_eager( subcontrib_contrib.event.of_type(subcontrib_event)) _apply_event_access_strategy(subcontrib_event_strategy) _apply_acl_entry_strategy( subcontrib_event_strategy.selectinload( subcontrib_event.acl_entries), EventPrincipal) subcontrib_session_strategy = subcontrib_contrib_strategy.contains_eager( subcontrib_contrib.session.of_type(subcontrib_session)) subcontrib_session_strategy.load_only('id', 'event_id', 'protection_mode') _apply_acl_entry_strategy( subcontrib_session_strategy.selectinload( subcontrib_session.acl_entries), SessionPrincipal) # session session_strategy = note_strategy.contains_eager(EventNote.session) session_strategy.load_only('id', 'event_id', 'protection_mode') session_event_strategy = session_strategy.contains_eager( Session.event.of_type(session_event)) _apply_event_access_strategy(session_event_strategy) session_event_strategy.selectinload(session_event.acl_entries) _apply_acl_entry_strategy( session_strategy.selectinload(Session.acl_entries), SessionPrincipal) note_filters = [ EventNote.html_matches(q), ~EventNote.is_deleted, db.or_(EventNote.link_type != LinkType.event, ~Event.is_deleted), db.or_(EventNote.link_type != LinkType.contribution, ~Contribution.is_deleted & ~contrib_event.is_deleted), db.or_( EventNote.link_type != LinkType.subcontribution, db.and_(~SubContribution.is_deleted, ~subcontrib_contrib.is_deleted, ~subcontrib_event.is_deleted)), db.or_(EventNote.link_type != LinkType.session, ~Session.is_deleted & ~session_event.is_deleted) ] if category_id is not None: note_filters.append( EventNote.event.has( Event.category_chain_overlaps(category_id))) if event_id is not None: note_filters.append(EventNote.event_id == event_id) query = ( EventNote.query.filter( *note_filters).options(note_strategy).outerjoin( EventNote.linked_event).outerjoin( EventNote.contribution).outerjoin( Contribution.event.of_type(contrib_event)). outerjoin(Contribution.session.of_type(contrib_session)).outerjoin( EventNote.subcontribution).outerjoin( SubContribution.contribution.of_type(subcontrib_contrib)). outerjoin( subcontrib_contrib.event.of_type(subcontrib_event)).outerjoin( subcontrib_contrib.session.of_type( subcontrib_session)).outerjoin( EventNote.session).outerjoin( Session.event.of_type(session_event))) objs, pagenav = self._paginate(query, page, EventNote.id, user, admin_override_enabled) query = (EventNote.query.filter(EventNote.id.in_( n.id for n in objs)).options( joinedload(EventNote.contribution), joinedload(EventNote.subcontribution).joinedload( SubContribution.contribution), joinedload(EventNote.event).options( undefer(Event.detailed_category_chain)), joinedload(EventNote.current_revision).joinedload( EventNoteRevision.user).joinedload('_affiliation'), )) notes_by_id = {n.id: n for n in query} notes = [notes_by_id[n.id] for n in objs] res = HTMLStrippingEventNoteSchema(many=True).dump(notes) return pagenav, EventNoteResultSchema(many=True).load(res)
def _process_args(self): self.event = Event.get(int(request.view_args['confId'])) if self.event is None: raise NotFound(_('An event with this ID does not exist.')) elif self.event.is_deleted: raise NotFound(_('This event has been deleted.'))
def migrate_event_managers(self): self.print_step('migrating event managers/creators') creator_updates = [] for event in committing_iterator(self._iter_events(), 5000): self.print_success('', event_id=event.id) ac = event._Conference__ac entries = {} # add creator as a manager try: creator = event._Conference__creator except AttributeError: # events created after the removal of the `self.__creator` assignment # should happen only on dev machines self.print_error( cformat('%{red!}Event has no creator attribute'), event_id=event.id) else: user = self.process_principal(event, entries, creator, 'Creator', 'green!', full_access=True) if user: creator_updates.append({ 'event_id': int(event.id), 'creator_id': user.id }) # add managers for manager in ac.managers: self.process_principal(event, entries, manager, 'Manager', 'blue!', full_access=True) # add email-based managers emails = getattr(ac, 'managersEmail', []) self.process_emails(event, entries, emails, 'Manager', 'green', full_access=True) # add registrars for registrar in getattr(event, '_Conference__registrars', []): self.process_principal(event, entries, registrar, 'Registrar', 'cyan', roles={'registration'}) # add submitters for submitter in getattr(ac, 'submitters', []): self.process_principal(event, entries, submitter, 'Submitter', 'magenta!', roles={'submit'}) # email-based (pending) submitters pqm = getattr(event, '_pendingQueuesMgr', None) if pqm is not None: emails = set(getattr(pqm, '_pendingConfSubmitters', [])) self.process_emails(event, entries, emails, 'Submitter', 'magenta', roles={'submit'}) db.session.add_all(entries.itervalues()) # assign creators if creator_updates: self.print_step('saving event creators') stmt = (Event.__table__.update().where( Event.id == db.bindparam('event_id')).values( creator_id=db.bindparam('creator_id'))) db.session.execute(stmt, creator_updates) updated = Event.find(Event.creator_id == None).update( {Event.creator_id: self.janitor.id}) # noqa db.session.commit() self.print_success('Set the janitor user {} for {} events'.format( self.janitor, updated), always=True)
def _build_report(self): event = Event.get_one(self.params['event_id'], is_deleted=False) new_material = get_nested_attached_items(event) self.material = {'tree': [self._format_data(new_material)]}
def search_attachments(self, q, user, page, category_id, event_id, admin_override_enabled): contrib_event = db.aliased(Event) contrib_session = db.aliased(Session) subcontrib_contrib = db.aliased(Contribution) subcontrib_session = db.aliased(Session) subcontrib_event = db.aliased(Event) session_event = db.aliased(Event) attachment_strategy = _apply_acl_entry_strategy( selectinload(Attachment.acl_entries), AttachmentPrincipal) folder_strategy = contains_eager(Attachment.folder) folder_strategy.load_only('id', 'protection_mode', 'link_type', 'category_id', 'event_id', 'linked_event_id', 'contribution_id', 'subcontribution_id', 'session_id') _apply_acl_entry_strategy( folder_strategy.selectinload(AttachmentFolder.acl_entries), AttachmentFolderPrincipal) # event event_strategy = folder_strategy.contains_eager( AttachmentFolder.linked_event) _apply_event_access_strategy(event_strategy) _apply_acl_entry_strategy( event_strategy.selectinload(Event.acl_entries), EventPrincipal) # contribution contrib_strategy = folder_strategy.contains_eager( AttachmentFolder.contribution) _apply_contrib_access_strategy(contrib_strategy) _apply_acl_entry_strategy( contrib_strategy.selectinload(Contribution.acl_entries), ContributionPrincipal) contrib_event_strategy = contrib_strategy.contains_eager( Contribution.event.of_type(contrib_event)) _apply_event_access_strategy(contrib_event_strategy) _apply_acl_entry_strategy( contrib_event_strategy.selectinload(contrib_event.acl_entries), EventPrincipal) contrib_session_strategy = contrib_strategy.contains_eager( Contribution.session.of_type(contrib_session)) contrib_session_strategy.load_only('id', 'event_id', 'protection_mode') _apply_acl_entry_strategy( contrib_session_strategy.selectinload(contrib_session.acl_entries), SessionPrincipal) # subcontribution subcontrib_strategy = folder_strategy.contains_eager( AttachmentFolder.subcontribution) subcontrib_strategy.load_only('id', 'contribution_id', 'title') subcontrib_contrib_strategy = subcontrib_strategy.contains_eager( SubContribution.contribution.of_type(subcontrib_contrib)) _apply_contrib_access_strategy(subcontrib_contrib_strategy) _apply_acl_entry_strategy( subcontrib_contrib_strategy.selectinload( subcontrib_contrib.acl_entries), ContributionPrincipal) subcontrib_event_strategy = subcontrib_contrib_strategy.contains_eager( subcontrib_contrib.event.of_type(subcontrib_event)) _apply_event_access_strategy(subcontrib_event_strategy) _apply_acl_entry_strategy( subcontrib_event_strategy.selectinload( subcontrib_event.acl_entries), EventPrincipal) subcontrib_session_strategy = subcontrib_contrib_strategy.contains_eager( subcontrib_contrib.session.of_type(subcontrib_session)) subcontrib_session_strategy.load_only('id', 'event_id', 'protection_mode') _apply_acl_entry_strategy( subcontrib_session_strategy.selectinload( subcontrib_session.acl_entries), SessionPrincipal) # session session_strategy = folder_strategy.contains_eager( AttachmentFolder.session) session_strategy.load_only('id', 'event_id', 'protection_mode') session_event_strategy = session_strategy.contains_eager( Session.event.of_type(session_event)) _apply_event_access_strategy(session_event_strategy) session_event_strategy.selectinload(session_event.acl_entries) _apply_acl_entry_strategy( session_strategy.selectinload(Session.acl_entries), SessionPrincipal) attachment_filters = [ Attachment.title_matches(q), ~Attachment.is_deleted, ~AttachmentFolder.is_deleted, AttachmentFolder.link_type != LinkType.category, db.or_( AttachmentFolder.link_type != LinkType.event, ~Event.is_deleted, ), db.or_(AttachmentFolder.link_type != LinkType.contribution, ~Contribution.is_deleted & ~contrib_event.is_deleted), db.or_( AttachmentFolder.link_type != LinkType.subcontribution, db.and_( ~SubContribution.is_deleted, ~subcontrib_contrib.is_deleted, ~subcontrib_event.is_deleted, )), db.or_(AttachmentFolder.link_type != LinkType.session, ~Session.is_deleted & ~session_event.is_deleted) ] if category_id is not None: attachment_filters.append( AttachmentFolder.event.has( Event.category_chain_overlaps(category_id))) if event_id is not None: attachment_filters.append(AttachmentFolder.event_id == event_id) query = ( Attachment.query.join( Attachment.folder).filter(*attachment_filters).options( folder_strategy, attachment_strategy, joinedload( Attachment.user).joinedload('_affiliation')).outerjoin( AttachmentFolder.linked_event).outerjoin( AttachmentFolder.contribution).outerjoin( Contribution.event.of_type(contrib_event)). outerjoin(Contribution.session.of_type(contrib_session)).outerjoin( AttachmentFolder.subcontribution).outerjoin( SubContribution.contribution.of_type(subcontrib_contrib)). outerjoin( subcontrib_contrib.event.of_type(subcontrib_event)).outerjoin( subcontrib_contrib.session.of_type( subcontrib_session)).outerjoin( AttachmentFolder.session).outerjoin( Session.event.of_type(session_event))) objs, pagenav = self._paginate(query, page, Attachment.id, user, admin_override_enabled) query = (Attachment.query.filter(Attachment.id.in_( a.id for a in objs)).options( joinedload(Attachment.folder).options( joinedload(AttachmentFolder.subcontribution), joinedload(AttachmentFolder.event).options( undefer(Event.detailed_category_chain))))) attachments_by_id = {a.id: a for a in query} attachments = [attachments_by_id[a.id] for a in objs] res = AttachmentSchema(many=True).dump(attachments) return pagenav, AttachmentResultSchema(many=True).load(res)
def create_event(category, event_type, data, add_creator_as_manager=True, features=None): from indico.modules.rb.operations.bookings import create_booking_for_event """Create a new event. :param category: The category in which to create the event :param event_type: An `EventType` value :param data: A dict containing data used to populate the event :param add_creator_as_manager: Whether the creator (current user) should be added as a manager :param features: A list of features that will be enabled for the event. If set, only those features will be used and the default feature set for the event type will be ignored. """ event = Event(category=category, type_=event_type) data.setdefault('creator', session.user) theme = data.pop('theme', None) create_booking = data.pop('create_booking', False) person_link_data = data.pop('person_link_data', {}) event.populate_from_dict(data) db.session.flush() event.person_link_data = person_link_data if theme is not None: layout_settings.set(event, 'timetable_theme', theme) if add_creator_as_manager: with event.logging_disabled: event.update_principal(event.creator, full_access=True) if features is not None: features_event_settings.set(event, 'enabled', features) db.session.flush() signals.event.created.send(event) logger.info('Event %r created in %r by %r ', event, category, session.user) event.log(EventLogRealm.event, EventLogKind.positive, 'Event', 'Event created', session.user) db.session.flush() if create_booking: room_id = data['location_data'].pop('room_id', None) if room_id: booking = create_booking_for_event(room_id, event) if booking: logger.info('Booking %r created for event %r', booking, event) log_data = { 'Room': booking.room.full_name, 'Date': booking.start_dt.strftime('%d/%m/%Y'), 'Times': '%s - %s' % (booking.start_dt.strftime('%H:%M'), booking.end_dt.strftime('%H:%M')) } event.log(EventLogRealm.event, EventLogKind.positive, 'Event', 'Room booked for the event', session.user, data=log_data) db.session.flush() return event
def migrate_event_stubs(self): self.print_step('migrating event stubs') for event_id in committing_iterator(self._iter_event_ids(), 5000): db.session.add(Event(id=int(event_id)))
def export_timetable(self, user): events = Event.find_all(Event.id.in_(map(int, self._idList)), ~Event.is_deleted) return {event.id: TimetableSerializer(event, management=False, user=user).serialize_timetable() for event in events}
def export_timetable(self, user): events = Event.find_all(Event.id.in_(map(int, self._idList)), ~Event.is_deleted) return {event.id: self._serialize_timetable(event, user) for event in events}
def create_event(category, event_type, data, add_creator_as_manager=True, features=None, cloning=False): """Create a new event. :param category: The category in which to create the event :param event_type: An `EventType` value :param data: A dict containing data used to populate the event :param add_creator_as_manager: Whether the creator (current user) should be added as a manager :param features: A list of features that will be enabled for the event. If set, only those features will be used and the default feature set for the event type will be ignored. :param cloning: Whether the event is created via cloning or not """ from indico.modules.rb.operations.bookings import create_booking_for_event event = Event(category=category, type_=event_type) data.setdefault('creator', session.user) theme = data.pop('theme', None) create_booking = data.pop('create_booking', False) person_link_data = data.pop('person_link_data', {}) event.populate_from_dict(data) db.session.flush() event.person_link_data = person_link_data if theme is not None: layout_settings.set(event, 'timetable_theme', theme) if add_creator_as_manager: with event.logging_disabled: event.update_principal(event.creator, full_access=True) if features is not None: features_event_settings.set(event, 'enabled', features) db.session.flush() signals.event.created.send(event, cloning=cloning) logger.info('Event %r created in %r by %r ', event, category, session.user) sep = ' \N{RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK} ' event.log(EventLogRealm.event, LogKind.positive, 'Event', 'Event created', session.user, data={ 'Category': sep.join(category.chain_titles) if category else None }) if category: category.log(CategoryLogRealm.events, LogKind.positive, 'Content', f'Event created: "{event.title}"', session.user, data={ 'ID': event.id, 'Type': orig_string(event.type_.title) }) db.session.flush() if create_booking: room_id = data['location_data'].pop('room_id', None) if room_id: booking = create_booking_for_event(room_id, event) if booking: logger.info('Booking %r created for event %r', booking, event) log_data = { 'Room': booking.room.full_name, 'Date': booking.start_dt.strftime('%d/%m/%Y'), 'Times': '{} - {}'.format(booking.start_dt.strftime('%H:%M'), booking.end_dt.strftime('%H:%M')) } event.log(EventLogRealm.event, LogKind.positive, 'Event', 'Room booked for the event', session.user, data=log_data) db.session.flush() return event
def event(self): return Event.get(int(self.event_id), is_deleted=False)
def category_events(self, catIds): from indico.modules.events import Event query = (Event.query.filter( ~Event.is_deleted, Event.category_chain.overlap(map(int, catIds)), Event.happens_between(self._fromDT, self._toDT))) return self._process(x.as_legacy for x in query)
def search_contribs(self, q, user, page, category_id, event_id, admin_override_enabled): # XXX: Ideally we would search in subcontributions as well, but our pagination # does not really work when we do not have a single unique ID contrib_filters = [ Contribution.title_matches(q) | Contribution.description_matches(q), ~Contribution.is_deleted, ~Event.is_deleted ] if category_id is not None: contrib_filters.append(Event.category_chain_overlaps(category_id)) if event_id is not None: contrib_filters.append(Contribution.event_id == event_id) query = (Contribution.query.filter(*contrib_filters).join( Contribution.event).options( load_only('id', 'session_id', 'event_id', 'protection_mode'), undefer(Contribution.effective_protection_mode), _apply_acl_entry_strategy( selectinload(Contribution.acl_entries), ContributionPrincipal), joinedload(Contribution.session).options( load_only('id', 'protection_mode', 'event_id'), selectinload(Session.acl_entries)), contains_eager('event').options( _apply_acl_entry_strategy(selectinload(Event.acl_entries), EventPrincipal)))) objs, pagenav = self._paginate(query, page, Contribution.id, user, admin_override_enabled) event_strategy = joinedload(Contribution.event) event_strategy.joinedload(Event.own_venue) event_strategy.joinedload(Event.own_room).options( raiseload('*'), joinedload('location')) event_strategy.undefer(Event.detailed_category_chain) session_strategy = joinedload(Contribution.session) session_strategy.joinedload(Session.own_venue) session_strategy.joinedload(Session.own_room).options( raiseload('*'), joinedload('location')) session_block_strategy = joinedload(Contribution.session_block) session_block_strategy.joinedload(SessionBlock.own_venue) session_block_strategy.joinedload(SessionBlock.own_room).options( raiseload('*'), joinedload('location')) session_block_session_strategy = session_block_strategy.joinedload( SessionBlock.session) session_block_session_strategy.joinedload(Session.own_venue) session_block_session_strategy.joinedload(Session.own_room).options( raiseload('*'), joinedload('location')) query = (Contribution.query.filter( Contribution.id.in_(c.id for c in objs)).options( selectinload(Contribution.person_links).joinedload( 'person').joinedload('user').load_only('is_system'), event_strategy, session_strategy, session_block_strategy, joinedload(Contribution.type), joinedload(Contribution.own_venue), joinedload(Contribution.own_room).options( raiseload('*'), joinedload('location')), joinedload(Contribution.timetable_entry), )) contribs_by_id = {c.id: c for c in query} contribs = [contribs_by_id[c.id] for c in objs] res = HTMLStrippingContributionSchema(many=True).dump(contribs) return pagenav, ContributionResultSchema(many=True).load(res)
def event(self): return Event.get_one(self.event_id, is_deleted=False)
def _events_query(self): return Event.find( Event.attachment_folders.any( AttachmentFolder.title.op('~')('^part\d+$')))
def _query_categ_events(categ, start_dt, end_dt): return (Event.query .with_parent(categ) .filter(Event.happens_between(start_dt, end_dt)) .options(load_only('id', 'start_dt', 'end_dt')))
def serialize_category_ical(category, user, event_filter): """Export the events in a category to iCal :param category: The category to export :param user: The user who needs to be able to access the events :param event_filter: A SQLalchemy criterion to restrict which events will be returned. Usually something involving the start/end date of the event. """ own_room_strategy = joinedload('own_room') own_room_strategy.load_only('building', 'floor', 'number', 'name') own_room_strategy.lazyload('owner') own_venue_strategy = joinedload('own_venue').load_only('name') query = (Event.query.filter(Event.category_chain_overlaps( category.id), ~Event.is_deleted, event_filter).options( load_only('id', 'category_id', 'start_dt', 'end_dt', 'title', 'description', 'own_venue_name', 'own_room_name', 'protection_mode', 'access_key'), subqueryload('acl_entries'), joinedload('person_links'), own_room_strategy, own_venue_strategy).order_by(Event.start_dt)) events = [e for e in query if e.can_access(user)] cal = ical.Calendar() cal.add('version', '2.0') cal.add('prodid', '-//CERN//INDICO//EN') now = now_utc(False) for event in events: url = url_for('event.conferenceDisplay', confId=event.id, _external=True) location = ('{} ({})'.format(event.room_name, event.venue_name) if event.venue_name and event.room_name else (event.venue_name or event.room_name)) cal_event = ical.Event() cal_event.add('uid', u'indico-event-{}@cern.ch'.format(event.id)) cal_event.add('dtstamp', now) cal_event.add('dtstart', event.start_dt) cal_event.add('dtend', event.end_dt) cal_event.add('url', url) cal_event.add('summary', event.title) cal_event.add('location', location) description = [] if event.person_links: speakers = [ u'{} ({})'.format(x.full_name, x.affiliation) if x.affiliation else x.full_name for x in event.person_links ] description.append(u'Speakers: {}'.format(u', '.join(speakers))) if event.description: desc_text = unicode( event.description) or u'<p/>' # get rid of RichMarkup try: description.append( unicode(html.fromstring(desc_text).text_content())) except ParserError: # this happens e.g. if desc_text contains only a html comment pass description.append(url) cal_event.add('description', u'\n'.join(description)) cal.add_component(cal_event) return BytesIO(cal.to_ical())