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).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).options(load_only('event_id'))) for person in query: data[person.event_id].add('abstract_person') return data
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).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 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 deserialize(self): if not self.force and self.data['fossir_version'] != fossir.__version__: click.secho( 'Version mismatch: trying to import event exported with {} to version {}' .format(self.data['fossir_version'], fossir.__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 fossir instance') self._associate_users_by_email(event) db.session.flush() return event
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='fossir 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 _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 _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 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 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 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 _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 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 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 get_events_managed_by(user, dt=None): """Gets the IDs of events where the user has management privs. :param user: A `User` :param dt: Only include events taking place on/after that date :return: A set of event ids """ query = (user.in_event_acls.join(Event).options( noload('user'), noload('local_group'), load_only('event_id')).filter( ~Event.is_deleted, Event.ends_after(dt)).filter( EventPrincipal.has_management_role('ANY'))) return {principal.event_id for principal 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 _create_event(id_=None, **kwargs): # 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) event = Event(id=id_, creator=dummy_user, acl_entries=set(), **kwargs) db.session.flush() return event
def session(self, idlist): event = Event.get(self._eventId, is_deleted=False) if not event: return [] idlist = set(map(int, idlist)) sessions = (Session.query.with_parent(event).filter( Session.id.in_(idlist), ~Session.is_deleted).all()) # Fallback for friendly_id sessions += (Session.query.with_parent(event).filter( Session.friendly_id.in_(idlist - {s.id for s in sessions}), ~Session.is_deleted).all()) self._detail_level = 'contributions' return self._build_sessions_api_data(sessions)
def is_feature_enabled(event, name): """Checks if a feature is enabled for an event. :param event: The event (or event ID) to check. :param name: The name of the feature. """ feature = get_feature_definition(name) enabled_features = features_event_settings.get(event, 'enabled') if enabled_features is not None: return feature.name in enabled_features else: if isinstance(event, (basestring, int, long)): event = Event.get(event) return event and feature.is_default_for_event(event)
def export(event_id, target_file): """Exports all data associated with an event. This exports the whole event as an archive which can be imported on another other fossir instance. Importing an event is only guaranteed to work if it was exported on the same fossir version. """ event = Event.get(event_id) if event is None: click.secho('This event does not exist', fg='red') sys.exit(1) elif event.is_deleted: click.secho('This event has been deleted', fg='yellow') click.confirm('Export it anyway?', abort=True) export_event(event, target_file)
def get_events_with_submitted_surveys(user, dt=None): """Gets the IDs of events where the user submitted a survey. :param user: A `User` :param dt: Only include events taking place on/after that date :return: A set of event ids """ from fossir.modules.events.surveys.models.surveys import Survey # Survey submissions are not stored in links anymore, so we need to get them directly query = (user.survey_submissions .options(load_only('survey_id')) .options(joinedload(SurveySubmission.survey).load_only('event_id')) .join(Survey) .join(Event) .filter(~Survey.is_deleted, ~Event.is_deleted, Event.ends_after(dt))) return {submission.survey.event_id for submission in query}
def get_events_with_paper_roles(user, dt=None): """ Get the IDs and PR roles of events where the user has any kind of paper reviewing privileges. :param user: A `User` :param dt: Only include events taking place on/after that date :return: A dict mapping event IDs to a set of roles """ paper_roles = {'paper_manager', 'paper_judge', 'paper_content_reviewer', 'paper_layout_reviewer'} role_criteria = [EventPrincipal.has_management_role(role, explicit=True) for role in paper_roles] query = (user.in_event_acls .join(Event) .options(noload('user'), noload('local_group'), load_only('event_id', 'roles')) .filter(~Event.is_deleted, Event.ends_after(dt)) .filter(db.or_(*role_criteria))) return {principal.event_id: set(principal.roles) & paper_roles for principal in query}
def get_events_with_linked_event_persons(user, dt=None): """ Returns a dict containing the event ids and role for all events where the user is a chairperson or (in case of a lecture) speaker. :param user: A `User` :param dt: Only include events taking place on/after that date """ query = (user.event_persons.with_entities( EventPerson.event_id, Event._type).join(Event, Event.id == EventPerson.event_id).filter( EventPerson.event_links.any()).filter(~Event.is_deleted, Event.ends_after(dt))) return { event_id: ('lecture_speaker' if event_type == EventType.lecture else 'conference_chair') for event_id, event_type in query }
def test_deleted_relationships(db, dummy_event): event = dummy_event assert not event.contributions assert not event.sessions s = Session(event=event, title='s') sd = Session(event=event, title='sd', is_deleted=True) c = Contribution(event=event, title='c', session=sd, duration=timedelta(minutes=30)) cd = Contribution(event=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 == event assert sd.event == event assert c.event == event assert cd.event == event assert sc.contribution == c assert scd.contribution == c
def get_object_from_args(args=None): """Retrieves 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.find_first(id=args['confId'], 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.find( SubContribution.id == args['subcontrib_id'], ~SubContribution.is_deleted, SubContribution.contribution.has(event=event, id=args['contrib_id'], is_deleted=False)).first() else: raise ValueError('Unexpected object type: {}'.format(object_type)) if obj is not None: return object_type, event, obj else: return object_type, None, None
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 _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 _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')))