def _process(self): from indico.modules.events.contributions import contribution_settings show_booking_warning = False if (config.ENABLE_ROOMBOOKING and not self.event.has_ended and self.event.room and not self.event.room_reservation_links): # Check if any of the managers of the event already have a booking that overlaps with the event datetime manager_ids = [p.user.id for p in self.event.acl_entries if p.user] has_overlap = (ReservationOccurrence.query .filter(ReservationOccurrence.is_valid, db.or_(Reservation.booked_for_id.in_(manager_ids), Reservation.created_by_id.in_(manager_ids)), db_dates_overlap(ReservationOccurrence, 'start_dt', self.event.start_dt_local, 'end_dt', self.event.end_dt_local), Reservation.room_id == self.event.room.id, ~Room.is_deleted) .join(Reservation) .join(Room) .has_rows()) show_booking_warning = not has_overlap has_reference_types = ReferenceType.query.has_rows() has_event_labels = EventLabel.query.has_rows() show_draft_warning = (self.event.type_ == EventType.conference and not contribution_settings.get(self.event, 'published') and (TimetableEntry.query.with_parent(self.event).has_rows() or Contribution.query.with_parent(self.event).has_rows())) return WPEventSettings.render_template('settings.html', self.event, 'settings', show_booking_warning=show_booking_warning, show_draft_warning=show_draft_warning, has_reference_types=has_reference_types, has_event_labels=has_event_labels)
def create_contribution_from_abstract(abstract, contrib_session=None): event = abstract.event contrib_person_links = set() person_link_attrs = {'_title', 'address', 'affiliation', 'first_name', 'last_name', 'phone', 'author_type', 'is_speaker', 'display_order'} for abstract_person_link in abstract.person_links: link = ContributionPersonLink(person=abstract_person_link.person) link.populate_from_attrs(abstract_person_link, person_link_attrs) contrib_person_links.add(link) if contrib_session: duration = contrib_session.default_contribution_duration else: duration = contribution_settings.get(event, 'default_duration') custom_fields_data = {'custom_{}'.format(field_value.contribution_field.id): field_value.data for field_value in abstract.field_values} return create_contribution(event, {'friendly_id': abstract.friendly_id, 'title': abstract.title, 'duration': duration, 'description': abstract.description, 'type': abstract.accepted_contrib_type, 'track': abstract.accepted_track, 'session': contrib_session, 'person_link_data': {link: True for link in contrib_person_links}}, custom_fields_data=custom_fields_data)
def create_contribution_from_abstract(abstract, contrib_session=None): event = abstract.event contrib_person_links = set() person_link_attrs = { '_title', 'address', 'affiliation', 'first_name', 'last_name', 'phone', 'author_type', 'is_speaker', 'display_order' } for abstract_person_link in abstract.person_links: link = ContributionPersonLink(person=abstract_person_link.person) link.populate_from_attrs(abstract_person_link, person_link_attrs) contrib_person_links.add(link) if contrib_session: duration = contrib_session.default_contribution_duration else: duration = contribution_settings.get(event, 'default_duration') custom_fields_data = { 'custom_{}'.format(field_value.contribution_field.id): field_value.data for field_value in abstract.field_values } return create_contribution(event, { 'friendly_id': abstract.friendly_id, 'title': abstract.title, 'duration': duration, 'description': abstract.description, 'type': abstract.accepted_contrib_type, 'track': abstract.accepted_track, 'session': contrib_session, 'person_link_data': {link: True for link in contrib_person_links} }, custom_fields_data=custom_fields_data)
def _check_access(self): RHDisplayEventBase._check_access(self) published = contribution_settings.get(self.event, 'published') if not published and not self.event.can_manage(session.user): raise NotFound( _('The contributions of this event have not been published yet' ))
def _render_template(self, selected_entry, **kwargs): published = contribution_settings.get(self.event, 'published') return self.view_class.render_template(self.template, self.event, selected_entry=selected_entry, published=published, **kwargs)
def _process(self): return self.view_class.render_template( 'display/contribution_list.html', self.event, timezone=self.event.display_tzinfo, published=contribution_settings.get(self.event, 'published'), **self.list_generator.get_list_kwargs())
def _check_access(self): RHDisplayEventBase._check_access(self) published = contribution_settings.get(self.event, 'published') if not published: raise NotFound( _("The contributions of this event have not been published yet" ))
def _process(self): inherited_location = self.event.location_data inherited_location['inheriting'] = True default_duration = contribution_settings.get(self.event, 'default_duration') contrib_form_class = make_contribution_form(self.event) form = contrib_form_class(obj=FormDefaults( location_data=inherited_location, duration=default_duration), event=self.event) if form.validate_on_submit(): # Create empty contribution so it can be compared to the new one in flash_if_unregistered contrib = Contribution() with flash_if_unregistered(self.event, lambda: contrib.person_links): contrib = create_contribution(self.event, *get_field_values(form.data)) flash( _("Contribution '{}' created successfully").format( contrib.title), 'success') tpl_components = self.list_generator.render_list(contrib) if tpl_components['hide_contrib']: self.list_generator.flash_info_message(contrib) return jsonify_data(**tpl_components) return jsonify_template('events/contributions/forms/contribution.html', form=form)
def _check_access(self): RHAbstractsBase._check_access(self) published = contribution_settings.get(self.event, 'published') if not published: raise NotFound( _('The contributions of this event have not been published yet' ))
def _has_access(self, user): event = Event.get(self._eventId, is_deleted=False) if event: can_manage = user is not None and event.can_manage(user) if not can_manage and not contribution_settings.get(event, 'published'): return False return True
def can_edit(self, user): # Submitters can edit their own contributions if configured from indico.modules.events.contributions import contribution_settings submitters_can_edit = contribution_settings.get( self.event, 'submitters_can_edit') return self.can_manage( user, permission=('submit' if submitters_can_edit else None))
def _check_access(self): RHDisplayEventBase._check_access(self) published = contribution_settings.get(self.event, 'published') if not self.contrib.can_access(session.user): raise Forbidden if not published: raise NotFound(_("The contributions of this event have not been published yet."))
def _process(self): form = ContributionDefaultDurationForm(duration=contribution_settings.get(self.event, 'default_duration')) if form.validate_on_submit(): contribution_settings.set(self.event, 'default_duration', form.duration.data) flash(_("Default contribution duration was changed successfully")) return jsonify_data() return jsonify_form(form)
def _check_access(self): RHDisplayEventBase._check_access(self) published = contribution_settings.get(self.event, 'published') if not published: raise NotFound(_("The contributions of this event have not been published yet.")) if not is_menu_entry_enabled(self.MENU_ENTRY_NAME, self.event): self._forbidden_if_not_admin()
def _process(self): form = AllowSubmitterEditsForm( allow=contribution_settings.get(self.event, 'submitters_can_edit')) if form.validate_on_submit(): contribution_settings.set(self.event, 'submitters_can_edit', form.allow.data) flash(_('Submitter edit settings changed successfully'), 'success') return jsonify_data() return jsonify_form(form)
def _check_access(self): if not self.contrib.can_access(session.user): # perform event access check since it may send the user to the access key or registration page RHDisplayEventBase._check_access(self) raise Forbidden published = contribution_settings.get(self.event, 'published') if not published and not self._can_view_unpublished(): raise NotFound( _('The contributions of this event have not been published yet.' ))
def _check_access(self): RHDisplayEventBase._check_access(self) if not self.subcontrib.can_access(session.user): raise Forbidden published = contribution_settings.get(self.event, 'published') if (not published and not self.event.can_manage(session.user) and not self.contrib.is_user_associated(session.user)): raise NotFound( _("The contributions of this event have not been published yet." ))
def _process(self): inherited_location = self.event.location_data inherited_location['inheriting'] = True default_duration = contribution_settings.get(self.event, 'default_duration') form = SessionForm(obj=FormDefaults(colors=get_random_color(self.event), location_data=inherited_location, default_contribution_duration=default_duration), event=self.event) if form.validate_on_submit(): new_session = create_session(self.event, form.data) return self._get_response(new_session) return jsonify_form(form)
def _check_access(self): RHDisplayEventBase._check_access(self) published = contribution_settings.get(self.event, 'published') if not published and not has_contributions_with_user_as_submitter( self.event, session.user): raise NotFound( _('The contributions of this event have not been published yet.' )) if not is_menu_entry_enabled(self.MENU_ENTRY_NAME, self.event): self._forbidden_if_not_admin()
def _check_access(self): if not self.subcontrib.can_access(session.user): # perform event access check since it may send the user to the access key or registration page RHDisplayEventBase._check_access(self) raise Forbidden published = contribution_settings.get(self.event, 'published') if (not published and not self.event.can_manage(session.user) and not self.subcontrib.is_user_associated(session.user)): raise NotFound( _("The contributions of this event have not been published yet." ))
def _process(self): inherited_location = self.event.location_data inherited_location['inheriting'] = True default_duration = contribution_settings.get(self.event, 'default_duration') form = SessionForm(obj=FormDefaults(colors=get_random_color(self.event), location_data=inherited_location, default_contribution_duration=default_duration), event=self.event) if form.validate_on_submit(): new_session = create_session(self.event, form.data) return self._get_response(new_session) return jsonify_form(form)
def create_contribution_from_abstract(abstract, contrib_session=None): from indico.modules.events.abstracts.settings import abstracts_settings event = abstract.event contrib_person_links = set() author_submission_rights = ( event.cfa.contribution_submitters == SubmissionRightsType.all) person_link_attrs = { '_title', 'address', 'affiliation', 'first_name', 'last_name', 'phone', 'author_type', 'is_speaker', 'display_order' } for abstract_person_link in abstract.person_links: link = ContributionPersonLink(person=abstract_person_link.person) link.populate_from_attrs(abstract_person_link, person_link_attrs) contrib_person_links.add(link) if contrib_session: duration = contrib_session.default_contribution_duration else: duration = contribution_settings.get(event, 'default_duration') custom_fields_data = { f'custom_{field_value.contribution_field.id}': field_value.data for field_value in abstract.field_values } contrib = create_contribution(event, { 'friendly_id': abstract.friendly_id, 'title': abstract.title, 'duration': duration, 'description': abstract.description, 'type': abstract.accepted_contrib_type, 'track': abstract.accepted_track, 'session': contrib_session, 'person_link_data': { link: (author_submission_rights or link.is_speaker) for link in contrib_person_links } }, custom_fields_data=custom_fields_data) if abstracts_settings.get(event, 'copy_attachments') and abstract.files: folder = AttachmentFolder.get_or_create_default(contrib) for abstract_file in abstract.files: attachment = Attachment(user=abstract.submitter, type=AttachmentType.file, folder=folder, title=abstract_file.filename) attachment.file = AttachmentFile( user=abstract.submitter, filename=abstract_file.filename, content_type=abstract_file.content_type) with abstract_file.open() as fd: attachment.file.save(fd) db.session.flush() return contrib
def _process(self): contrib = (Contribution.query.filter_by(id=self.contrib.id).options( joinedload('type'), joinedload('session'), joinedload('subcontributions'), joinedload('timetable_entry').lazyload('*')).one()) can_manage = self.event.can_manage(session.user) owns_abstract = contrib.abstract.user_owns( session.user) if contrib.abstract else None field_values = filter_field_values(contrib.field_values, can_manage, owns_abstract) return self.view_class.render_template( 'display/contribution_display.html', self.event, contribution=contrib, show_author_link=_author_page_active(self.event), field_values=field_values, page_title=contrib.title, published=contribution_settings.get(self.event, 'published'))
def _process(self): inherited_location = self.event.location_data inherited_location['inheriting'] = True default_duration = contribution_settings.get(self.event, 'default_duration') contrib_form_class = make_contribution_form(self.event) form = contrib_form_class(obj=FormDefaults(location_data=inherited_location, duration=default_duration), event=self.event) if form.validate_on_submit(): # Create empty contribution so it can be compared to the new one in flash_if_unregistered contrib = Contribution() with flash_if_unregistered(self.event, lambda: contrib.person_links): contrib = create_contribution(self.event, *get_field_values(form.data)) flash(_("Contribution '{}' created successfully").format(contrib.title), 'success') tpl_components = self.list_generator.render_list(contrib) if tpl_components['hide_contrib']: self.list_generator.flash_info_message(contrib) return jsonify_data(**tpl_components) return jsonify_template('events/contributions/forms/contribution.html', form=form)
def _process(self): self.event.preload_all_acl_entries() if self.theme is None: event_info = serialize_event_info(self.event) timetable_data = TimetableSerializer( self.event).serialize_timetable(strip_empty_days=True) timetable_settings = layout_settings.get( self.event, 'timetable_theme_settings') return self.view_class.render_template( 'display.html', self.event, event_info=event_info, timetable_data=timetable_data, timetable_settings=timetable_settings, timetable_layout=self.timetable_layout, published=contribution_settings.get(self.event, 'published')) else: return self.view_class_simple(self, self.event, self.theme, self.theme_override).display()
def create_contribution_from_abstract(abstract, contrib_session=None): from indico.modules.events.abstracts.settings import abstracts_settings event = abstract.event contrib_person_links = set() person_link_attrs = {'_title', 'address', 'affiliation', 'first_name', 'last_name', 'phone', 'author_type', 'is_speaker', 'display_order'} for abstract_person_link in abstract.person_links: link = ContributionPersonLink(person=abstract_person_link.person) link.populate_from_attrs(abstract_person_link, person_link_attrs) contrib_person_links.add(link) if contrib_session: duration = contrib_session.default_contribution_duration else: duration = contribution_settings.get(event, 'default_duration') custom_fields_data = {'custom_{}'.format(field_value.contribution_field.id): field_value.data for field_value in abstract.field_values} contrib = create_contribution(event, {'friendly_id': abstract.friendly_id, 'title': abstract.title, 'duration': duration, 'description': abstract.description, 'type': abstract.accepted_contrib_type, 'track': abstract.accepted_track, 'session': contrib_session, 'person_link_data': {link: True for link in contrib_person_links}}, custom_fields_data=custom_fields_data) if abstracts_settings.get(event, 'copy_attachments') and abstract.files: folder = AttachmentFolder.get_or_create_default(contrib) for abstract_file in abstract.files: attachment = Attachment(user=abstract.submitter, type=AttachmentType.file, folder=folder, title=abstract_file.filename) attachment.file = AttachmentFile(user=abstract.submitter, filename=abstract_file.filename, content_type=abstract_file.content_type) with abstract_file.open() as fd: attachment.file.save(fd) db.session.flush() return contrib
def _process_GET(self): return jsonify(contribution_settings.get(self.event, 'published'))
def _visible_timetable(event): return contribution_settings.get(event, 'published')
def _default_duration(self): return contribution_settings.get(self.event, 'default_duration')
def _serialize_timetable(self, event, user): if not contribution_settings.get( event, 'published') and not event.can_manage(user): return {} return TimetableSerializer(event, management=False, user=user).serialize_timetable()
def _build_event_api_data(self, event): can_manage = self.user is not None and event.can_manage(self.user) data = self._build_event_api_data_base(event) data.update({ '_fossil': self.fossils_mapping['event'].get(self._detail_level), 'categoryId': event.category_id, 'category': event.category.title, 'note': build_note_api_data(event.note), 'roomFullname': event.room_name, 'url': event.external_url, 'creationDate': self._serialize_date(event.created_dt), 'creator': self._serialize_person(event.creator, person_type='Avatar', can_manage=can_manage), 'hasAnyProtection': event.effective_protection_mode != ProtectionMode.public, 'roomMapURL': event.room.map_url if event.room else None, 'folders': build_folders_api_data(event), 'chairs': self._serialize_persons(event.person_links, person_type='ConferenceChair', can_manage=can_manage), 'material': build_material_legacy_api_data(event) + filter(None, [build_note_legacy_api_data(event.note)]), 'keywords': event.keywords, }) event_category_path = event.category.chain visibility = {'id': '', 'name': 'Everywhere'} if event.visibility is None: pass # keep default elif event.visibility == 0: visibility['name'] = 'Nowhere' elif event.visibility: try: path_segment = event_category_path[-event.visibility] except IndexError: pass else: visibility['id'] = path_segment['id'] visibility['name'] = path_segment['title'] data['visibility'] = visibility if can_manage: data['allowed'] = self._serialize_access_list(event) allow_details = contribution_settings.get(event, 'published') or can_manage if self._detail_level in {'contributions', 'subcontributions' } and allow_details: data['contributions'] = [] for contribution in event.contributions: include_subcontribs = self._detail_level == 'subcontributions' serialized_contrib = self._serialize_contribution( contribution, include_subcontribs) data['contributions'].append(serialized_contrib) elif self._detail_level == 'sessions' and allow_details: # Contributions without a session data['contributions'] = [] for contribution in event.contributions: if not contribution.session: serialized_contrib = self._serialize_contribution( contribution) data['contributions'].append(serialized_contrib) data['sessions'] = [] for session_ in event.sessions: data['sessions'].extend(self._build_session_api_data(session_)) if self._occurrences: data['occurrences'] = fossilize(self._calculate_occurrences( event, self._fromDT, self._toDT, pytz.timezone(config.DEFAULT_TIMEZONE)), {Period: IPeriodFossil}, tz=self._tz, naiveTZ=config.DEFAULT_TIMEZONE) # check whether the plugins want to add/override any data for update in values_from_signal( signals.event.metadata_postprocess.send('http-api', event=event, data=data), as_list=True): data.update(update) return data
class CategoryEventFetcher(IteratedDataFetcher, SerializerBase): def __init__(self, user, hook): super().__init__(user, hook) self._eventType = hook._eventType self._occurrences = hook._occurrences self._location = hook._location self._room = hook._room self.user = user self._detail_level = get_query_parameter(request.args.to_dict(), ['d', 'detail'], 'events') if self._detail_level not in ('events', 'contributions', 'subcontributions', 'sessions'): raise HTTPAPIError(f'Invalid detail level: {self._detail_level}', 400) def _calculate_occurrences(self, event, from_dt, to_dt, tz): start_dt = max(from_dt, event.start_dt) if from_dt else event.start_dt end_dt = min(to_dt, event.end_dt) if to_dt else event.end_dt for day in iterdays(start_dt, end_dt): first_start, last_end = find_event_day_bounds(event, day.date()) if first_start is not None: yield Period(first_start, last_end) def _get_query_options(self, detail_level): acl_user_strategy = joinedload('acl_entries').joinedload('user') # remote group membership checks will trigger a load on _all_emails # but not all events use this so there's no need to eager-load them # acl_user_strategy.noload('_primary_email') # acl_user_strategy.noload('_affiliation') creator_strategy = joinedload('creator') contributions_strategy = subqueryload('contributions') contributions_strategy.subqueryload('references') if detail_level in {'subcontributions', 'sessions'}: contributions_strategy.subqueryload('subcontributions').subqueryload('references') sessions_strategy = subqueryload('sessions') options = [acl_user_strategy, creator_strategy] if detail_level in {'contributions', 'subcontributions', 'sessions'}: options.append(contributions_strategy) if detail_level == 'sessions': options.append(sessions_strategy) options.append(undefer('effective_protection_mode')) return options def category(self, idlist, format): try: idlist = list(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 = query.has_rows() return { 'eventCategories': self._build_category_path_data(ids), 'moreFutureEvents': has_future_events } 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 _filter_event(self, event): if self._room or self._location or self._eventType: if self._eventType and event.type_.name != self._eventType: return False if self._location: name = event.venue_name if not name or not fnmatch.fnmatch(name.lower(), self._location.lower()): return False if self._room: name = event.room_name if not name or not fnmatch.fnmatch(name.lower(), self._room.lower()): return False return True def _update_query(self, query): order = get_query_parameter(request.args.to_dict(), ['o', 'order']) desc = get_query_parameter(request.args.to_dict(), ['c', 'descending']) == 'yes' limit = get_query_parameter(request.args.to_dict(), ['n', 'limit']) offset = get_query_parameter(request.args.to_dict(), ['O', 'offset']) col = { 'start': Event.start_dt, 'end': Event.end_dt, 'id': Event.id, 'title': Event.title }.get(order) if col: query = query.order_by(col.desc() if desc else col) if limit: query = query.limit(limit) if offset: query = query.offset(offset) return query def serialize_events(self, events): return list(map(self._build_event_api_data, events)) def _serialize_category_path(self, category): visibility = {'id': None, 'name': 'Everywhere'} path = [self._serialize_path_entry(category_data) for category_data in category.chain] if category.visibility is not None: try: path_segment = path[-category.visibility] except IndexError: pass else: visibility['id'] = path_segment['id'] visibility['name'] = path_segment['name'] path.append({'visibility': visibility}) return path def _serialize_path_entry(self, category_data): return { '_fossil': 'categoryMetadata', 'type': 'Category', 'name': category_data['title'], 'id': category_data['id'], 'url': url_for('categories.display', category_id=category_data['id'], _external=True) } def _build_category_path_data(self, ids): return [{'_type': 'CategoryPath', 'categoryId': category.id, 'path': self._serialize_category_path(category)} for category in Category.query.filter(Category.id.in_(ids)).options(undefer('chain'))] def _build_event_api_data(self, event): can_manage = self.user is not None and event.can_manage(self.user) data = self._build_event_api_data_base(event) material_data = build_material_legacy_api_data(event) if legacy_note_material := build_note_legacy_api_data(event.note): material_data.append(legacy_note_material) data.update({ '_fossil': self.fossils_mapping['event'].get(self._detail_level), 'categoryId': event.category_id, 'category': event.category.title, 'note': build_note_api_data(event.note), 'roomFullname': event.room_name, 'url': event.external_url, 'creationDate': self._serialize_date(event.created_dt), 'creator': self._serialize_person(event.creator, person_type='Avatar', can_manage=can_manage), 'hasAnyProtection': event.effective_protection_mode != ProtectionMode.public, 'roomMapURL': event.room.map_url if event.room else None, 'folders': build_folders_api_data(event), 'chairs': self._serialize_persons(event.person_links, person_type='ConferenceChair', can_manage=can_manage), 'material': material_data, 'keywords': event.keywords, }) event_category_path = event.category.chain visibility = {'id': '', 'name': 'Everywhere'} if event.visibility is None: pass # keep default elif event.visibility == 0: visibility['name'] = 'Nowhere' elif event.visibility: try: path_segment = event_category_path[-event.visibility] except IndexError: pass else: visibility['id'] = path_segment['id'] visibility['name'] = path_segment['title'] data['visibility'] = visibility if can_manage: data['allowed'] = self._serialize_access_list(event) allow_details = contribution_settings.get(event, 'published') or can_manage if self._detail_level in {'contributions', 'subcontributions'}: data['contributions'] = [] if allow_details: for contribution in event.contributions: include_subcontribs = self._detail_level == 'subcontributions' serialized_contrib = self._serialize_contribution(contribution, include_subcontribs) data['contributions'].append(serialized_contrib) elif self._detail_level == 'sessions': data['contributions'] = [] data['sessions'] = [] if allow_details: # Contributions without a session for contribution in event.contributions: if not contribution.session: serialized_contrib = self._serialize_contribution(contribution) data['contributions'].append(serialized_contrib) for session_ in event.sessions: data['sessions'].extend(self._build_session_api_data(session_)) if self._occurrences: data['occurrences'] = fossilize(self._calculate_occurrences(event, self._fromDT, self._toDT, pytz.timezone(config.DEFAULT_TIMEZONE)), {Period: IPeriodFossil}, tz=self._tz, naiveTZ=config.DEFAULT_TIMEZONE) # check whether the plugins want to add/override any data for update in values_from_signal( signals.event.metadata_postprocess.send('http-api', event=event, data=data), as_list=True): data.update(update) return data
def _boa_visible(event): return (config.LATEX_ENABLED and event.has_feature('abstracts') and contribution_settings.get(event, 'published'))
def should_show_draft_warning(event): return (event.type_ == EventType.conference and not contribution_settings.get(event, 'published') and (TimetableEntry.query.with_parent(event).has_rows() or Contribution.query.with_parent(event).has_rows()))
def _default_duration(self): return contribution_settings.get(self.event, 'default_duration')