def migrate(self): self.op.create_table( 'favorites', Column('id', Integer, Sequence("favorites_id_seq"), primary_key=True), Column('admin_unit_id', String(UNIT_ID_LENGTH), index=True, nullable=False), Column('int_id', Integer, index=True, nullable=False), Column('userid', String(USER_ID_LENGTH), index=True), Column('position', Integer, index=True), Column('title', String(CONTENT_TITLE_LENGTH), nullable=False), Column('is_title_personalized', Boolean, default=False, nullable=False), Column('portal_type', String(PORTAL_TYPE_LENGTH)), Column('icon_class', String(CSS_CLASS_LENGTH)), Column('plone_uid', String(UID_LENGTH)), Column('created', UTCDateTime(timezone=True), default=utcnow_tz_aware), Column('modified', UTCDateTime(timezone=True), default=utcnow_tz_aware, onupdate=utcnow_tz_aware)) self.ensure_sequence_exists('favorites_id_seq')
class Favorite(Base): __tablename__ = 'favorites' favorite_id = Column('id', Integer, Sequence("favorites_id_seq"), primary_key=True) admin_unit_id = Column(String(UNIT_ID_LENGTH), index=True, nullable=False) int_id = Column(Integer, index=True, nullable=False) oguid = composite(Oguid, admin_unit_id, int_id) userid = Column(String(USER_ID_LENGTH), index=True) position = Column(Integer, index=True) title = Column(String(CONTENT_TITLE_LENGTH), nullable=False) is_title_personalized = Column(Boolean, default=False, nullable=False) portal_type = Column(String(PORTAL_TYPE_LENGTH)) icon_class = Column(String(CSS_CLASS_LENGTH)) plone_uid = Column(String(UID_LENGTH)) created = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware) modified = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware, onupdate=utcnow_tz_aware)
def migrate(self): self.op.create_table( 'digests', Column("id", Integer, Sequence('digest_id_seq'), primary_key=True), Column("userid", String(255), nullable=False), Column("last_dispatch", UTCDateTime(timezone=True))) self.ensure_sequence_exists('digest_id_seq')
class ArchiveMixin(object): @declared_attr def contact_id(self): return Column(Integer, ForeignKey('contacts.id'), nullable=False) actor_id = Column(String(USER_ID_LENGTH), nullable=False) created = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware)
class Activity(Base, Translatable): __tablename__ = 'activities' __translatable__ = { 'locales': SUPPORTED_LOCALES, 'fallback_locale': DEFAULT_LOCALE } locale = DEFAULT_LOCALE id = Column('id', Integer, Sequence("activities_id_seq"), primary_key=True) kind = Column(String(255), nullable=False) actor_id = Column(String(USER_ID_LENGTH), nullable=False) created = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware) resource_id = Column(Integer, ForeignKey('resources.id'), nullable=False) resource = relationship("Resource", backref="activities") def __repr__(self): return u'<Activity {} on {} >'.format(self.kind, repr(self.resource)) def create_notifications(self): """Create a notification for every resource watcher. For the activity's actor, who has created the activity, a notification is usually unnecessary or disruptive. We therefore only create a notification for the activity's actor if he has enabled the notify_own_actions'setting. """ notifications = [] for userid in self.get_users_for_watchers(): if (self.is_current_user(userid) and not self.user_wants_own_action_notifications(userid)): continue notifications.append(Notification(userid=userid, activity=self)) return notifications def get_users_for_watchers(self): users = [] for watcher in self.resource.watchers: users += watcher.get_user_ids() return set(users) def get_notifications_for_watcher_roles(self, roles): """Returns a list of activities notifications, but only those where the watchers watch the resource in one of the given roles. """ return Notification.query.by_subscription_roles(roles, self).all() def is_current_user(self, user_id): return user_id == self.actor_id def user_wants_own_action_notifications(self, userid): return UserSettings.get_setting_for_user(userid, 'notify_own_actions')
class Digest(Base): __tablename__ = 'digests' digest_id = Column('id', Integer, Sequence('digest_id_seq'), primary_key=True) userid = Column(String(USER_ID_LENGTH), nullable=False) last_dispatch = Column(UTCDateTime(timezone=True))
class Lock(Base): __tablename__ = 'locks' __table_args__ = (UniqueConstraint('object_id', 'object_type', 'lock_type'), {}) lock_id = Column("id", Integer, Sequence("locks_id_seq"), primary_key=True) object_id = Column(Integer) object_type = Column(String(100), index=True) creator = Column(String(USER_ID_LENGTH), index=True) time = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware, index=True) lock_type = Column(String(100)) @property def token(self): return '{}:{}'.format(self.object_type, self.object_id) def is_valid(self): return self.time >= lowest_valid()
class ReminderSetting(Base): __tablename__ = 'reminder_settings' reminder_setting_id = Column('id', Integer, Sequence('reminder_setting_id_seq'), primary_key=True) task_id = Column(Integer, ForeignKey('tasks.id'), nullable=False) task = relationship("Task", backref="reminder_settings") actor_id = Column(String(USER_ID_LENGTH), index=True, nullable=False) option_type = Column(String(255), nullable=False) remind_day = Column(Date, nullable=False) created = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware) def __repr__(self): return u'<ReminderSetting {} for {} for {} on {} >'.format( self.reminder_setting_id, self.actor_id, repr(self.task), self.remind_day)
class Favorite(Base): __tablename__ = 'favorites' __table_args__ = ( UniqueConstraint('admin_unit_id', 'int_id', 'userid', name='ix_favorites_unique'), {}) favorite_id = Column('id', Integer, Sequence("favorites_id_seq"), primary_key=True) admin_unit_id = Column(String(UNIT_ID_LENGTH), index=True, nullable=False) int_id = Column(Integer, index=True, nullable=False) oguid = composite(Oguid, admin_unit_id, int_id) userid = Column(String(USER_ID_LENGTH), index=True) position = Column(Integer, index=True) title = Column(String(CONTENT_TITLE_LENGTH), nullable=False) is_title_personalized = Column(Boolean, default=False, nullable=False) portal_type = Column(String(PORTAL_TYPE_LENGTH)) icon_class = Column(String(CSS_CLASS_LENGTH)) plone_uid = Column(String(UID_LENGTH)) created = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware) modified = Column(UTCDateTime(timezone=True), default=utcnow_tz_aware, onupdate=utcnow_tz_aware) def serialize(self, portal_url): return { '@id': self.api_url(portal_url), 'portal_type': self.portal_type, 'favorite_id': self.favorite_id, 'oguid': self.oguid.id, 'uid': self.plone_uid, 'title': self.title, 'icon_class': self.icon_class, 'target_url': self.get_target_url(), 'tooltip_url': self.get_tooltip_url(), 'position': self.position, 'admin_unit': AdminUnit.query.get(self.admin_unit_id).title} def api_url(self, portal_url): return '{}/@favorites/{}/{}'.format( portal_url, self.userid, self.favorite_id) @property def tooltip_view(self): if is_bumblebeeable(self): return 'tooltip' def get_tooltip_url(self): url = self.get_target_url() if self.tooltip_view: return u'{}/{}'.format(url, self.tooltip_view) return None def get_target_url(self): admin_unit = AdminUnit.query.get(self.admin_unit_id) return u'{}/resolve_oguid/{}'.format(admin_unit.public_url, self.oguid) @staticmethod def truncate_title(title): return safe_unicode(title)[:CONTENT_TITLE_LENGTH]
class Meeting(Base, SQLFormSupport): STATE_PENDING = State('pending', is_default=True, title=_('pending', default='Pending')) STATE_HELD = State('held', title=_('held', default='Held')) STATE_CLOSED = State('closed', title=_('closed', default='Closed')) workflow = Workflow( [STATE_PENDING, STATE_HELD, STATE_CLOSED], [ CloseTransition('pending', 'closed', title=_('close_meeting', default='Close meeting')), Transition('pending', 'held', title=_('hold', default='Hold meeting'), visible=False), CloseTransition('held', 'closed', title=_('close_meeting', default='Close meeting')), Transition('closed', 'held', title=_('reopen', default='Reopen'), condition=is_word_meeting_implementation_enabled) ], show_in_actions_menu=True, transition_controller=MeetingTransitionController, ) __tablename__ = 'meetings' meeting_id = Column("id", Integer, Sequence("meeting_id_seq"), primary_key=True) committee_id = Column(Integer, ForeignKey('committees.id'), nullable=False) committee = relationship("Committee", backref='meetings') location = Column(String(256)) title = Column(UnicodeCoercingText) start = Column('start_datetime', UTCDateTime(timezone=True), nullable=False) end = Column('end_datetime', UTCDateTime(timezone=True)) workflow_state = Column(String(WORKFLOW_STATE_LENGTH), nullable=False, default=workflow.default_state.name) modified = Column(UTCDateTime(timezone=True), nullable=False, default=utcnow_tz_aware) meeting_number = Column(Integer) presidency = relationship( 'Member', primaryjoin="Member.member_id==Meeting.presidency_id") presidency_id = Column(Integer, ForeignKey('members.id')) secretary = relationship( 'Member', primaryjoin="Member.member_id==Meeting.secretary_id") secretary_id = Column(Integer, ForeignKey('members.id')) other_participants = Column(UnicodeCoercingText) participants = relationship('Member', secondary=meeting_participants, order_by='Member.lastname, Member.firstname', backref='meetings') dossier_admin_unit_id = Column(String(UNIT_ID_LENGTH), nullable=False) dossier_int_id = Column(Integer, nullable=False) dossier_oguid = composite(Oguid, dossier_admin_unit_id, dossier_int_id) agenda_items = relationship("AgendaItem", order_by='AgendaItem.sort_order', backref='meeting') protocol_document_id = Column(Integer, ForeignKey('generateddocuments.id')) protocol_document = relationship( 'GeneratedProtocol', uselist=False, backref=backref('meeting', uselist=False), primaryjoin= "GeneratedProtocol.document_id==Meeting.protocol_document_id") protocol_start_page_number = Column(Integer) agendaitem_list_document_id = Column(Integer, ForeignKey('generateddocuments.id')) agendaitem_list_document = relationship( 'GeneratedAgendaItemList', uselist=False, backref=backref('meeting', uselist=False), primaryjoin= "GeneratedAgendaItemList.document_id==Meeting.agendaitem_list_document_id" ) # define relationship here using a secondary table to keep # GeneratedDocument as simple as possible and avoid that it actively # knows about all its relationships excerpt_documents = relationship( 'GeneratedExcerpt', secondary=meeting_excerpts, ) def initialize_participants(self): """Set all active members of our committee as participants of this meeting. """ self.participants = [ membership.member for membership in Membership.query.for_meeting(self) ] def __repr__(self): return '<Meeting at "{}">'.format(self.start) def generate_meeting_number(self): """Generate meeting number for self. This method locks the current period of this meeting to protect its meeting_sequence_number against concurrent updates. """ period = Period.query.get_current_for_update(self.committee) self.meeting_number = period.get_next_meeting_sequence_number() def generate_decision_numbers(self): """Generate decision numbers for each agenda item of this meeting. This method locks the current period of this meeting to protect its decision_sequence_number against concurrent updates. """ period = Period.query.get_current_for_update(self.committee) for agenda_item in self.agenda_items: agenda_item.generate_decision_number(period) def update_protocol_document(self): """Update or create meeting's protocol.""" from opengever.meeting.command import CreateGeneratedDocumentCommand from opengever.meeting.command import MergeDocxProtocolCommand from opengever.meeting.command import ProtocolOperations from opengever.meeting.command import UpdateGeneratedDocumentCommand if self.has_protocol_document( ) and not self.protocol_document.is_locked(): # The protocol should never be changed when it is no longer locked: # the user probably has made changes manually. return operations = ProtocolOperations() if is_word_meeting_implementation_enabled(): command = MergeDocxProtocolCommand( self.get_dossier(), self, operations, lock_document_after_creation=True) else: if self.has_protocol_document(): command = UpdateGeneratedDocumentCommand( self.protocol_document, self, operations) else: command = CreateGeneratedDocumentCommand( self.get_dossier(), self, operations, lock_document_after_creation=True) command.execute() def unlock_protocol_document(self): if not self.protocol_document: return self.protocol_document.unlock_document() def hold(self): if self.workflow_state == 'held': return self.generate_meeting_number() self.generate_decision_numbers() self.workflow_state = 'held' def close(self): """Closes a meeting means set the meeting in the closed state. - generate and set the meeting number - generate decision numbers for each agenda_item - close each agenda item (generates proposal excerpt and change workflow state) - update and unlock the protocol document """ self.hold() for agenda_item in self.agenda_items: agenda_item.close() self.update_protocol_document() self.unlock_protocol_document() self.workflow_state = 'closed' @property def css_class(self): return 'contenttype-opengever-meeting-meeting' def is_editable(self): return self.get_state() in [self.STATE_PENDING, self.STATE_HELD] def is_agendalist_editable(self): return self.get_state() == self.STATE_PENDING def has_protocol_document(self): return self.protocol_document is not None def has_agendaitem_list_document(self): return self.agendaitem_list_document is not None @property def wrapper_id(self): return 'meeting-{}'.format(self.meeting_id) def _get_title(self, prefix): return u"{}-{}".format(translate(prefix, context=getRequest()), self.get_title()) def _get_filename(self, prefix): normalizer = getUtility(IIDNormalizer) return u"{}-{}.docx".format(translate(prefix, context=getRequest()), normalizer.normalize(self.get_title())) def get_protocol_title(self): return self._get_title(_("Protocol")) def get_excerpt_title(self): return self._get_title(_("Protocol Excerpt")) def get_agendaitem_list_title(self): return self._get_title( _(u'label_agendaitem_list', default=u'Agendaitem list')) def get_protocol_filename(self): return self._get_filename(_("Protocol")) def get_excerpt_filename(self): return self._get_filename(_("Protocol Excerpt")) def get_agendaitem_list_filename(self): return self._get_filename( _(u'label_agendaitem_list', default=u'Agendaitem list')) def get_protocol_template(self): return self.committee.get_protocol_template() def get_excerpt_template(self): return self.committee.get_excerpt_template() def get_agendaitem_list_template(self): return self.committee.get_agendaitem_list_template() @property def physical_path(self): return '/'.join((self.committee.physical_path, self.wrapper_id)) def execute_transition(self, name): self.workflow.execute_transition(self, self, name) def can_execute_transition(self, name): return self.workflow.can_execute_transition(self, name) def get_state(self): return self.workflow.get_state(self.workflow_state) def update_model(self, data): """Manually set the modified timestamp when updating meetings.""" super(Meeting, self).update_model(data) self.modified = utcnow_tz_aware() def get_title(self): return self.title def get_date(self): return api.portal.get_localized_time(datetime=self.start) def get_start(self): """Returns the start datetime in localized format. """ return api.portal.get_localized_time(datetime=self.start, long_format=True) def get_end(self): """Returns the end datetime in localized format. """ if self.end: return api.portal.get_localized_time(datetime=self.end, long_format=True) return None def get_start_time(self): return self._get_localized_time(self.start) def get_end_time(self): if not self.end: return '' return self._get_localized_time(self.end) def _get_localized_time(self, date): if not date: return '' return api.portal.get_localized_time(datetime=date, time_only=True) def schedule_proposal(self, proposal): assert proposal.committee == self.committee proposal.schedule(self) self.reorder_agenda_items() def schedule_text(self, title, is_paragraph=False): self.agenda_items.append( AgendaItem(title=title, is_paragraph=is_paragraph)) self.reorder_agenda_items() @require_word_meeting_feature def schedule_ad_hoc(self, title): committee = self.committee.resolve_committee() ad_hoc_template = committee.get_ad_hoc_template() if not ad_hoc_template: raise MissingAdHocTemplate meeting_dossier = self.get_dossier() if not api.user.get_current().checkPermission( 'opengever.document: Add document', meeting_dossier): raise MissingMeetingDossierPermissions document_title = _(u'title_ad_hoc_document', default=u'Ad hoc agenda item ${title}', mapping={u'title': title}) ad_hoc_document = CreateDocumentCommand( context=meeting_dossier, filename=ad_hoc_template.file.filename, data=ad_hoc_template.file.data, content_type=ad_hoc_template.file.contentType, title=translate(document_title, context=getRequest())).execute() agenda_item = AgendaItem(title=title, document=ad_hoc_document, is_paragraph=False) self.agenda_items.append(agenda_item) self.reorder_agenda_items() return agenda_item def _set_agenda_item_order(self, new_order): agenda_items_by_id = OrderedDict( (item.agenda_item_id, item) for item in self.agenda_items) agenda_items = [] for agenda_item_id in new_order: agenda_item = agenda_items_by_id.pop(agenda_item_id, None) if agenda_item: agenda_items.append(agenda_item) agenda_items.extend(agenda_items_by_id.values()) self.agenda_items = agenda_items def reorder_agenda_items(self, new_order=None): if new_order: self._set_agenda_item_order(new_order) sort_order = 1 number = 1 for agenda_item in self.agenda_items: agenda_item.sort_order = sort_order sort_order += 1 if not agenda_item.is_paragraph: agenda_item.number = '{}.'.format(number) number += 1 def get_submitted_link(self): return self._get_link(self.get_submitted_admin_unit(), self.submitted_physical_path) def get_link(self): url = self.get_url() link = u'<a href="{0}" title="{1}" class="{2}">{1}</a>'.format( url, escape_html(self.get_title()), self.css_class) return link def get_url(self, context=None, view='view'): elements = [ self.committee.get_admin_unit().public_url, self.physical_path ] if view: elements.append(view) return '/'.join(elements) def get_dossier_url(self): return self.dossier_oguid.get_url() def get_dossier(self): return self.dossier_oguid.resolve_object()
class Meeting(Base, SQLFormSupport): STATE_PENDING = State('pending', is_default=True, title=_('pending', default='Pending')) STATE_HELD = State('held', title=_('held', default='Held')) STATE_CLOSED = State('closed', title=_('closed', default='Closed')) STATE_CANCELLED = State('cancelled', title=_('cancelled', default='Cancelled')) workflow = Workflow( [STATE_PENDING, STATE_HELD, STATE_CLOSED, STATE_CANCELLED], [ CloseTransition('pending', 'closed', title=_('close_meeting', default='Close meeting')), Transition('pending', 'held', title=_('hold', default='Hold meeting'), visible=False), CloseTransition('held', 'closed', title=_('close_meeting', default='Close meeting')), Transition('closed', 'held', title=_('reopen', default='Reopen')), CancelTransition( 'pending', 'cancelled', title=_('cancel', default='Cancel')), ], show_in_actions_menu=True, transition_controller=MeetingTransitionController, ) __tablename__ = 'meetings' meeting_id = Column("id", Integer, Sequence("meeting_id_seq"), primary_key=True) committee_id = Column(Integer, ForeignKey('committees.id'), nullable=False) committee = relationship("Committee", backref='meetings') location = Column(String(256)) title = Column(UnicodeCoercingText) start = Column('start_datetime', UTCDateTime(timezone=True), nullable=False) end = Column('end_datetime', UTCDateTime(timezone=True)) workflow_state = Column(String(WORKFLOW_STATE_LENGTH), nullable=False, default=workflow.default_state.name) modified = Column(UTCDateTime(timezone=True), nullable=False, default=utcnow_tz_aware) meeting_number = Column(Integer) presidency = relationship( 'Member', primaryjoin="Member.member_id==Meeting.presidency_id") presidency_id = Column(Integer, ForeignKey('members.id')) secretary_id = Column(String(USER_ID_LENGTH), ForeignKey(User.userid)) secretary = relationship(User, primaryjoin=User.userid == secretary_id) other_participants = Column(UnicodeCoercingText) participants = relationship('Member', secondary=meeting_participants, order_by='Member.lastname, Member.firstname', backref='meetings') dossier_admin_unit_id = Column(String(UNIT_ID_LENGTH), nullable=False) dossier_int_id = Column(Integer, nullable=False) dossier_oguid = composite(Oguid, dossier_admin_unit_id, dossier_int_id) agenda_items = relationship("AgendaItem", order_by='AgendaItem.sort_order', backref='meeting') protocol_document_id = Column(Integer, ForeignKey('generateddocuments.id')) protocol_document = relationship( 'GeneratedProtocol', uselist=False, backref=backref('meeting', uselist=False), primaryjoin= "GeneratedProtocol.document_id==Meeting.protocol_document_id") protocol_start_page_number = Column(Integer) agendaitem_list_document_id = Column(Integer, ForeignKey('generateddocuments.id')) agendaitem_list_document = relationship( 'GeneratedAgendaItemList', uselist=False, backref=backref('meeting', uselist=False), primaryjoin= "GeneratedAgendaItemList.document_id==Meeting.agendaitem_list_document_id" ) def was_protocol_manually_edited(self): """checks whether the protocol has been manually edited or not""" if not self.has_protocol_document(): return False document = self.protocol_document.resolve_document() return not self.protocol_document.is_up_to_date(document) def get_other_participants_list(self): if self.other_participants is not None: return filter( len, map(lambda value: value.strip(), self.other_participants.split('\n'))) else: return [] def initialize_participants(self): """Set all active members of our committee as participants of this meeting. """ self.participants = [ membership.member for membership in Membership.query.for_meeting(self) ] @property def absentees(self): return [ membership.member for membership in Membership.query.for_meeting(self) if membership.member not in set(self.participants) ] def __repr__(self): return '<Meeting at "{}">'.format(self.start) def generate_meeting_number(self): """Generate meeting number for self. This method locks the current period of this meeting to protect its meeting_sequence_number against concurrent updates. """ period = Period.query.get_current_for_update(self.committee) self.meeting_number = period.get_next_meeting_sequence_number() def get_meeting_number(self): # Before the meeting is held, it will not have a meeting number. # In that case we do not want to format it with the period title prefixed if not self.meeting_number: return None period = Period.query.get_for_meeting(self) if not period: return str(self.meeting_number) title = period.title return '{} / {}'.format(title, self.meeting_number) def generate_decision_numbers(self): """Generate decision numbers for each agenda item of this meeting. This method locks the current period of this meeting to protect its decision_sequence_number against concurrent updates. """ period = Period.query.get_current_for_update(self.committee) for agenda_item in self.agenda_items: agenda_item.generate_decision_number(period) def update_protocol_document(self, overwrite=False): """Update or create meeting's protocol.""" from opengever.meeting.command import MergeDocxProtocolCommand from opengever.meeting.command import ProtocolOperations operations = ProtocolOperations() command = MergeDocxProtocolCommand(self.get_dossier(), self, operations) command.execute(overwrite=overwrite) return command def hold(self): if self.workflow_state == 'held': return self.generate_meeting_number() self.generate_decision_numbers() self.workflow_state = 'held' def close(self): """Closes a meeting means set the meeting in the closed state. - generate and set the meeting number - generate decision numbers for each agenda_item - close each agenda item (generates proposal excerpt and change workflow state) - generate or update the protocol if necessary """ self.hold() assert not self.get_undecided_agenda_items(), \ 'All agenda items must be decided before a meeting is closed.' try: self.update_protocol_document() except SablonProcessingFailed: msg = _(u'Error while processing Sablon template') api.portal.show_message(msg, api.portal.get().REQUEST, type='error') return False self.workflow_state = 'closed' return True @property def css_class(self): return 'contenttype-opengever-meeting-meeting' def is_editable(self): committee = self.committee.resolve_committee() if not api.user.has_permission('Modify portal content', obj=committee): return False return self.is_active() def is_agendalist_editable(self): if not self.is_editable(): return False return self.is_pending() def is_pending(self): return self.get_state() == self.STATE_PENDING def is_active(self): return self.get_state() in [self.STATE_HELD, self.STATE_PENDING] def is_closed(self): return self.get_state() == self.STATE_CLOSED def has_protocol_document(self): return self.protocol_document is not None def has_agendaitem_list_document(self): return self.agendaitem_list_document is not None @property def wrapper_id(self): return 'meeting-{}'.format(self.meeting_id) def _get_title(self, prefix): return u"{}-{}".format(translate(prefix, context=getRequest()), self.get_title()) def _get_filename(self, prefix): normalizer = getUtility(IFileNameNormalizer, name='gever_filename_normalizer') return u"{}-{}.docx".format(translate(prefix, context=getRequest()), normalizer.normalize(self.get_title())) def get_protocol_title(self): return self._get_title(_("Protocol")) def get_excerpt_title(self): return self._get_title(_("Protocol Excerpt")) def get_agendaitem_list_title(self): return self._get_title( _(u'label_agendaitem_list', default=u'Agendaitem list')) def get_protocol_filename(self): return self._get_filename(_("Protocol")) def get_excerpt_filename(self): return self._get_filename(_("Protocol Excerpt")) def get_agendaitem_list_filename(self): return self._get_filename( _(u'label_agendaitem_list', default=u'Agendaitem list')) def get_protocol_header_template(self): return self.committee.get_protocol_header_template() def get_protocol_suffix_template(self): return self.committee.get_protocol_suffix_template() def get_agenda_item_header_template(self): return self.committee.get_agenda_item_header_template() def get_agenda_item_suffix_template(self): return self.committee.get_agenda_item_suffix_template() def get_agendaitem_list_template(self): return self.committee.get_agendaitem_list_template() @property def physical_path(self): return '/'.join((self.committee.physical_path, self.wrapper_id)) def execute_transition(self, name): self.workflow.execute_transition(self, self, name) def can_execute_transition(self, name): return self.workflow.can_execute_transition(self, name) def get_state(self): return self.workflow.get_state(self.workflow_state) def update_model(self, data): """Manually set the modified timestamp when updating meetings.""" super(Meeting, self).update_model(data) self.modified = utcnow_tz_aware() meeting_dossier = self.get_dossier() title = data.get('title') if meeting_dossier and title: meeting_dossier.title = title meeting_dossier.reindexObject() def get_title(self): return self.title def get_date(self): return api.portal.get_localized_time(datetime=self.start) def get_start(self): """Returns the start datetime in localized format. """ return api.portal.get_localized_time(datetime=self.start, long_format=True) def get_end(self): """Returns the end datetime in localized format. """ if self.end: return api.portal.get_localized_time(datetime=self.end, long_format=True) return None def get_start_time(self): return self._get_localized_time(self.start) def get_end_time(self): if not self.end: return None return self._get_localized_time(self.end) def get_undecided_agenda_items(self): """Return a filtered list of this meetings agenda items, containing only the items which are not in a "decided" workflow state. """ def is_not_paragraph(agenda_item): return not agenda_item.is_paragraph def is_not_decided(agenda_item): return not agenda_item.is_completed() return filter(is_not_decided, filter(is_not_paragraph, self.agenda_items)) def _get_localized_time(self, date): if not date: return None return api.portal.get_localized_time(datetime=date, time_only=True) def schedule_proposal(self, proposal): assert proposal.committee == self.committee proposal.schedule(self) def schedule_text(self, title, is_paragraph=False, description=None): self.agenda_items.append( AgendaItem(title=title, description=description, is_paragraph=is_paragraph)) self.reorder_agenda_items() def schedule_ad_hoc(self, title, template_id=None, description=None): committee = self.committee.resolve_committee() if template_id is None: ad_hoc_template = committee.get_ad_hoc_template() else: from opengever.meeting.vocabulary import ProposalTemplatesForCommitteeVocabulary vocabulary_factory = ProposalTemplatesForCommitteeVocabulary() vocabulary = vocabulary_factory(committee) templates = [ term.value for term in vocabulary if term.value.getId() == template_id ] assert 1 == len(templates) ad_hoc_template = templates[0] if not ad_hoc_template: raise MissingAdHocTemplate meeting_dossier = self.get_dossier() if not api.user.get_current().checkPermission( 'opengever.document: Add document', meeting_dossier): raise MissingMeetingDossierPermissions ad_hoc_document = CreateDocumentCommand( context=meeting_dossier, filename=ad_hoc_template.file.filename, data=ad_hoc_template.file.data, content_type=ad_hoc_template.file.contentType, title=title).execute() agenda_item = AgendaItem(title=title, description=description, document=ad_hoc_document, is_paragraph=False) self.agenda_items.append(agenda_item) self.reorder_agenda_items() return agenda_item def _set_agenda_item_order(self, new_order): agenda_items_by_id = OrderedDict( (item.agenda_item_id, item) for item in self.agenda_items) agenda_items = [] for agenda_item_id in new_order: agenda_item = agenda_items_by_id.pop(agenda_item_id, None) if agenda_item: agenda_items.append(agenda_item) agenda_items.extend(agenda_items_by_id.values()) self.agenda_items = agenda_items def reorder_agenda_items(self, new_order=None): if new_order: self._set_agenda_item_order(new_order) sort_order = 1 number = 1 for agenda_item in self.agenda_items: agenda_item.sort_order = sort_order sort_order += 1 if not agenda_item.is_paragraph: agenda_item.item_number = number number += 1 def get_submitted_link(self): return self._get_link(self.get_submitted_admin_unit(), self.submitted_physical_path) def get_link(self): url = self.get_url() if api.user.has_permission('View', obj=self.committee.resolve_committee()): link = u'<a href="{0}" title="{1}" class="{2}">{1}</a>'.format( url, escape_html(self.get_title()), self.css_class) else: link = u'<span title="{0}" class="{1}">{0}</a>'.format( escape_html(self.get_title()), self.css_class) return link def get_url(self, context=None, view='view'): elements = [ self.committee.get_admin_unit().public_url, self.physical_path ] if view: elements.append(view) return '/'.join(elements) def get_dossier_url(self): return self.dossier_oguid.get_url() def get_dossier(self): return self.dossier_oguid.resolve_object()