class Report(VisualisableElement, Entity): """Report of ballot class""" name = renamer() electors = SharedMultipleProperty('electors') voters = SharedMultipleProperty('voters') subjects = SharedMultipleProperty('subjects') processes = SharedMultipleProperty('processes') ballot = SharedUniqueProperty('ballot', 'report') def __init__(self, ballottype, electors, subjects, **kwargs): kwargs['subjects'] = subjects kwargs['electors'] = electors super(Report, self).__init__(**kwargs) self.ballottype = BALLOT_TYPES[ballottype](self, **kwargs) if 'vote_process_id' in kwargs: self.ballottype.vote_process_id = kwargs['vote_process_id'] self.result = None self.calculated = False def calculate_votes(self): """Return the result of ballot""" if not self.calculated: votes = self.ballot.ballot_box.votes self.result = self.ballottype.calculate_votes(votes) self.calculated = True else: return self.result def get_electeds(self): """Return the elected subject""" if not self.calculated: self.calculate_votes() return self.ballottype.get_electeds(self.result) def he_voted(self, user): mask = getattr(user, 'mask', None) return user in self.voters or (mask and mask in self.voters) def is_elector(self, user): mask = getattr(user, 'mask', None) return user in self.electors or (mask and mask in self.electors) def get_elector(self, user): if not self.is_elector(user): return None if user in self.electors: return user mask = getattr(user, 'mask', None) if mask and mask in self.electors: return mask return None
class CorrelableEntity(Entity): """ A Correlable entity is an entity that can be correlated. A correlation is an abstract association between source entity and targets entities. """ source_correlations = SharedMultipleProperty('source_correlations', 'source') target_correlations = SharedMultipleProperty('target_correlations', 'targets') @property def correlations(self): """Return all source correlations and target correlations""" result = [c.target for c in self.source_correlations] result.extend([c.source for c in self.target_correlations]) return list(set(result)) @property def all_source_related_contents(self): lists_targets = [(c.targets, c) for c in self.source_correlations] return [(target, c) for targets, c in lists_targets for target in targets] @property def all_target_related_contents(self): return [(c.source, c) for c in self.target_correlations] @property def all_related_contents(self): related_contents = self.all_source_related_contents related_contents.extend(self.all_target_related_contents) return related_contents @property def contextualized_contents(self): lists_contents = [(c.targets, c) for c in self.contextualized_correlations] lists_contents = [(target, c) for targets, c in lists_contents for target in targets] lists_contents.extend([(c.source, c) for c in self.contextualized_correlations]) return lists_contents def get_related_contents(self, type_=None, tags=[]): if type_ is None and not tags: return self.all_related_contents return [(content, c) for content, c in self.all_related_contents if (type_ is None or c.type == type_) and (not tags or any( t in tags for t in c.tags))]
class FilmSynopses(SearchableEntity): """FilmSynopses class""" type_title = _('Film synopsis') icon = 'lac-icon icon-film-synopses' templates = {'default': 'lac:views/templates/film_synopses_result.pt', 'bloc': 'lac:views/templates/film_synopses_result_bloc.pt'} name = renamer() picture = CompositeUniqueProperty('picture') author = SharedUniqueProperty('author', 'contents') artists = SharedMultipleProperty('artists', 'creations') directors = SharedMultipleProperty('directors', 'productions') def __init__(self, **kwargs): self._presentation_text = None super(FilmSynopses, self).__init__(**kwargs) self.set_data(kwargs) def _init_presentation_text(self): self._presentation_text = html_to_text( getattr(self, 'abstract', '')) def __setattr__(self, name, value): super(FilmSynopses, self).__setattr__(name, value) if name == 'abstract': self._init_presentation_text() @property def artists_ids(self): return [str(get_oid(a)) for a in self.artists] @property def directors_ids(self): return [str(get_oid(a)) for a in self.directors] @property def relevant_data(self): result = super(FilmSynopses, self).relevant_data result.extend([', '.join([a.title for a in self.directors]), ', '.join([a.title for a in self.artists])]) return result def presentation_text(self, nb_characters=400): text = getattr(self, '_presentation_text', None) if text is None: self._init_presentation_text() text = getattr(self, '_presentation_text', '') return text[:nb_characters]+'...'
class CinemaReview(BaseReview): """Review class""" type_title = _('Cinema review') icon = 'lac-icon icon-reviwe-cinema' templates = { 'default': 'lac:views/templates/review_result.pt', 'bloc': 'lac:views/templates/review_result_bloc.pt' } name = renamer() directors = SharedMultipleProperty('directors', 'productions') @property def appreciation_title(self): return APPRECIATIONS.get(getattr(self, 'appreciation', ''), '') @property def directors_ids(self): return [str(get_oid(a)) for a in self.directors] @property def relevant_data(self): result = super(CinemaReview, self).relevant_data result.extend([', '.join([a.title for a in self.directors])]) return result
class User(OriginUser, Entity): groups = SharedMultipleProperty('groups', 'members') def __init__(self, password=None, email=None, tzname=None, locale=None, **kwargs): OriginUser.__init__(self, password, email, tzname, locale) Entity.__init__(self, **kwargs) def email_password_reset(self, request, message=None): """ Sends a password reset email.""" root = request.virtual_root sitename = acquire(root, 'title', None) or 'Dace' principals = find_service(self, 'principals') reset = principals.add_reset(self) # XXX should this really point at an SDI URL? reseturl = request.resource_url(reset) if not self.email: raise ValueError('User does not possess a valid email address.') _message = message if _message is None: _message = Message( subject='Account information for %s' % sitename, recipients=[self.email], body=render( 'dace:objectofcollaboration/principal/templates/resetpassword_email.pt', dict(reseturl=reseturl))) mailer = get_mailer(request) mailer.send(_message)
class Correlation(Debatable): """Correlation class""" name = renamer() source = SharedUniqueProperty('source', 'source_correlations') targets = SharedMultipleProperty('targets', 'target_correlations') context = SharedUniqueProperty('context', 'contextualized_correlations') author = SharedUniqueProperty('author') channels = CompositeMultipleProperty('channels', 'subject') comments = CompositeMultipleProperty('comments') def __init__(self, **kwargs): super(Correlation, self).__init__(**kwargs) self.set_data(kwargs) self.type = CorrelationType.weak self.tags = PersistentList() # self.addtoproperty('channels', Channel(title=_("General"))) @property def ends(self): """Return the ends of the correlation""" result = list(self.targets) result.append(self.source) return result @property def channel(self): channels = getattr(self, 'channels', []) return channels[0] if channels else None @property def type_name(self): return CorrelationType.type_name(self.type)
class Object1(Object): composition_u = CompositeUniqueProperty('composition_u', 'shared2_u', False) composition_m = CompositeMultipleProperty('composition_m', 'shared21_u', False) shared_u = SharedUniqueProperty('shared_u', 'shared22_u', False) shared_m = SharedMultipleProperty('shared_m', 'shared23_u', False)
class Keyword(VisualisableElement, Entity): """Keyword class""" name = renamer() referenced_elements = SharedMultipleProperty('referenced_elements', 'keywords') def __init__(self, **kwargs): super(Keyword, self).__init__(**kwargs) self.set_data(kwargs)
class FlowNode(BPMNElement): incoming = SharedMultipleProperty('incoming', 'target', False) outgoing = SharedMultipleProperty('outgoing', 'source', False) workitems = CompositeMultipleProperty('workitems', None, False) process = SharedUniqueProperty('process', 'nodes', False) def __init__(self, definition, **kwargs): super(FlowNode,self).__init__( definition, **kwargs) def prepare(self): registry = get_current_registry() registry.notify(ActivityPrepared(self)) def __call__(self, transaction): self.start(transaction) def find_executable_paths(self, source_path, source): pass# pragma: no cover def start(self, transaction): raise NotImplementedError# pragma: no cover def play(self, transitions): registry = get_current_registry() registry.notify(ActivityFinished(self)) self.process.play_transitions(self, transitions) def replay_path(self, decision, transaction): pass# pragma: no cover def stop(self): pass# pragma: no cover @property def definition(self): return self.process.definition[self.__name__] def __repr__(self):# pragma: no cover return "%s(%r)" % ( self.__class__.__name__, self.id )
class ParticipativeEntity(Entity): """ A Participative entity is an entity that can be improved""" contributors = SharedMultipleProperty('contributors', 'contributions') def add_contributors(self, contributors): for contributor in contributors: if contributor not in self.contributors: self.addtoproperty('contributors', contributor) def replace_by(self, source): pass
class Group(Entity): members = SharedMultipleProperty('members', 'groups') def __init__(self, **kwargs): super(Group, self).__init__(**kwargs) self.set_data(kwargs) def reindex(self): super(Group, self).reindex() for member in self.members: member.reindex()
class ServiceableEntity(Entity): services = SharedMultipleProperty('services', 'perimeter') def __init__(self, **kwargs): super(ServiceableEntity, self).__init__(**kwargs) def get_services(self, kind=None, user=None): services = self.services if user: services = [s for s in self.services if can_access(user, s)] if not services or kind is None: return services # registry_content = get_current_registry().content return [s for s in services if s.definition.service_id == kind] def get_all_services(self, context=None, user=None, site=None, kinds=None, validate=True, delegation=True): if user is None: user = get_current() if site is None: site = get_site_folder(True) if context is None: context = self if kinds is None: kinds = SERVICES services = {} for service in kinds: services[service] = self.get_services(service) result = {} for service, items in services.items(): result[service] = [] for item in items: if context is item.perimeter and \ (not validate or (validate and item.is_valid(context, user))) \ and (not delegation or (delegation and item.delegated_to(user))): result[service].append(item) result[service] = list(set(result[service])) return {service: value for service, value in result.items() if value}
class FlowNodeDefinition(BPMNElementDefinition): incoming = SharedMultipleProperty('incoming', 'target', False) outgoing = SharedMultipleProperty('outgoing', 'source', False) process = SharedUniqueProperty('process', 'nodes', False) factory = Attribute("factory") def create(self): return self.factory(self) def __init__(self, **kwargs): super(FlowNodeDefinition, self).__init__(**kwargs) self.groups = [] if 'groups' in kwargs: self.groups = kwargs['groups'] def find_startable_paths(self, source_path, source): decision_path = source_path.clone() source_transaction = source_path.transaction.__parent__ source_transaction.remove_subtransaction(source_path.transaction) yield decision_path def __repr__(self): # pragma: no cover return "<%s %r>" % (self.__class__.__name__, self.__name__)
class BaseReview(VisualisableElement, SearchableEntity): """Base Review class""" type_title = _('Base review') thumbnail = CompositeUniqueProperty('thumbnail') picture = CompositeUniqueProperty('picture') author = SharedUniqueProperty('author', 'contents') artists = SharedMultipleProperty('artists', 'creations') def __init__(self, **kwargs): self._presentation_text = None super(BaseReview, self).__init__(**kwargs) self.set_data(kwargs) def _init_presentation_text(self): self._presentation_text = html_article_to_text( getattr(self, 'article', '')) def __setattr__(self, name, value): super(BaseReview, self).__setattr__(name, value) if name == 'article': self._init_presentation_text() @property def relevant_data(self): result = super(BaseReview, self).relevant_data result.extend([', '.join([a.title for a in self.artists])]) return result @property def artists_ids(self): return [str(get_oid(a)) for a in self.artists] def presentation_text(self, nb_characters=400): text = getattr(self, '_presentation_text', None) if text is None: self._init_presentation_text() text = getattr(self, '_presentation_text', '') return text[:nb_characters]+'...' def get_visibility_filter(self): result = super(BaseReview, self).get_visibility_filter() authors = [self.author] if self.author else [] result.update({ 'contribution_filter': {'authors': authors}}) return result
class Preregistration(VisualisableElement, Entity): """Preregistration class""" icon = 'typcn typcn-user-add' templates = { 'default': 'novaideo:views/templates/preregistration_result.pt', 'bloc': 'novaideo:views/templates/preregistration_result.pt' } name = renamer() ballots = CompositeMultipleProperty('ballots') ballot_processes = SharedMultipleProperty('ballot_processes') organization = SharedUniqueProperty('organization') def __init__(self, **kwargs): super(Preregistration, self).__init__(**kwargs) initial_password = kwargs.pop('password', None) if initial_password: initial_password = User.pwd_manager.encode(initial_password) self.initial_password = initial_password self.set_data(kwargs) self.title = self.first_name + ' ' + \ self.last_name def init_deadline(self, date): self.deadline_date = date\ + datetime.timedelta(seconds=DEADLINE_PREREGISTRATION) return self.deadline_date def get_deadline_date(self): if getattr(self, 'deadline_date', None) is not None: return self.deadline_date self.deadline_date = self.created_at\ + datetime.timedelta(seconds=DEADLINE_PREREGISTRATION) return self.deadline_date def has_trusted_email(self, trusted_emails): email = getattr(self, 'email', None) if email and trusted_emails: return any(email.find(t) >= 0 for t in trusted_emails) return True @property def is_expired(self): return datetime.datetime.now(tz=pytz.UTC) > self.get_deadline_date()
class Organization(VisualisableElement, Group): """Organization class""" templates = { 'default': 'novaideo:views/templates/organization_result.pt', 'popover': 'novaideo:views/templates/organization_popover.pt', 'small': 'novaideo:views/templates/small_organization_result.pt', 'header': 'novaideo:views/templates/organization_header.pt', } icon = 'glyphicon glyphicon-home' name = renamer() managers = managers() members = SharedMultipleProperty('members', 'organization') logo = CompositeUniqueProperty('logo') cover_picture = CompositeUniqueProperty('cover_picture') def __init__(self, **kwargs): super(Organization, self).__init__(**kwargs) self.set_data(kwargs)
class Order(VisualisableElement, Entity): """Order class""" name = renamer() icon = 'glyphicon glyphicon-barcode' templates = { 'default': 'lac:views/templates/order_result.pt', 'bloc': 'lac:views/templates/order_result.pt' } products = SharedMultipleProperty('products', 'order') customeraccount = SharedUniqueProperty('customeraccount', 'orders') def __init__(self, **kwargs): super(Order, self).__init__(**kwargs) self.set_data(kwargs) @property def total(self): return sum([float(product.get_price()) for product in self.products])
class Channel(Commentable): """Channel class""" type_title = _('Channel') icon = 'icon novaideo-icon icon-idea' templates = {'default': 'novaideo:views/templates/channel_result.pt'} name = renamer() members = SharedMultipleProperty('members', 'following_channels') subject = SharedUniqueProperty('subject', 'channels') def __init__(self, **kwargs): super(Channel, self).__init__(**kwargs) self.set_data(kwargs) self._comments_at = OOBTree() def add_comment(self, comment): self._comments_at[comment.created_at] = get_oid(comment) def remove_comment(self, comment): self._comments_at.pop(comment.created_at) def get_comments_between(self, start, end): return list(self._comments_at.values(min=start, max=end)) def get_subject(self, user=None): subject = self.subject return subject if subject else getattr(self, '__parent__', None) def get_title(self, user=None): title = getattr(self, 'title', '') if not title: return getattr(self.get_subject(user), 'title', None) return title def is_discuss(self): return self.subject.__class__.__name__.lower() == 'person'
class Amendment(CorrelableEntity, SearchableEntity, DuplicableEntity, PresentableEntity, Debatable): """Amendment class""" type_title = _('Amendment') icon = 'icon novaideo-icon icon-amendment' name = renamer() templates = { 'default': 'novaideo:views/templates/amendment_result.pt', 'popover': 'novaideo:views/templates/amendment_popover.pt', 'bloc': 'novaideo:views/templates/amendment_bloc.pt' } author = SharedUniqueProperty('author') proposal = SharedUniqueProperty('proposal', 'amendments') attached_files = SharedMultipleProperty('attached_files') def __init__(self, **kwargs): super(Amendment, self).__init__(**kwargs) self.explanations = PersistentDict() self.set_data(kwargs) self.addtoproperty('channels', Channel()) @property def is_published(self): return 'submitted' in self.state @property def working_group(self): return self.proposal.working_group @property def authors(self): return [self.author] @property def challenge(self): return getattr(self.proposal, 'challenge', None) def _init_presentation_text(self): self._presentation_text = html_to_text(getattr(self, 'text', '')) def __setattr__(self, name, value): super(Amendment, self).__setattr__(name, value) if name == 'text': self._init_presentation_text() def presentation_text(self, nb_characters=400): text = getattr(self, '_presentation_text', None) if text is None: self._init_presentation_text() text = getattr(self, '_presentation_text', '') return truncate_text(text, nb_characters) # @region.cache_on_arguments() def get_used_ideas(self): """Return used ideas""" result = [] if not hasattr(self, 'explanations'): return result for explanation in self.explanations.values(): if explanation['intention'] is not None: try: result.extend( Intention.get_explanation_ideas( explanation['intention'])) except Exception: pass return list(set(result)) @property def related_ideas(self): """Return added ideas""" result = [] for explanation in self.explanations.values(): if explanation['intention'] is not None: try: result.extend( Intention.get_explanation_data( explanation['intention'])['related_ideas']) except Exception: pass return list(set(result))
class Challenge(SearchableEntity, CorrelableEntity, PresentableEntity, ExaminableEntity, Node, Emojiable, SignalableEntity, Debatable): """Challenge class""" type_title = _('Challenge') icon = 'ion-trophy' templates = { 'default': 'novaideo:views/templates/challenge_result.pt', 'bloc': 'novaideo:views/templates/challenge_card.pt', 'small': 'novaideo:views/templates/small_challenge_result.pt', 'popover': 'novaideo:views/templates/challenge_popover.pt', 'card': 'novaideo:views/templates/challenge_card.pt' } name = renamer() author = SharedUniqueProperty('author', 'challenges') image = CompositeUniqueProperty('image') proposals = SharedMultipleProperty('proposals', 'challenge') ideas = SharedMultipleProperty('ideas', 'challenge') questions = SharedMultipleProperty('questions', 'challenge') attached_files = CompositeMultipleProperty('attached_files') invited_users = SharedMultipleProperty('invited_users') url_files = CompositeMultipleProperty('url_files') def __init__(self, **kwargs): super(Challenge, self).__init__(**kwargs) self.set_data(kwargs) self.addtoproperty('channels', Channel()) self.urls = PersistentDict({}) def __setattr__(self, name, value): super(Challenge, self).__setattr__(name, value) if name in ('deadline', 'published_at', 'created_at') and value: self.init_total_days() @property def related_contents(self): return [content[0] for content in self.all_related_contents] @property def challenge(self): return self @property def transformed_from(self): """Return all related contents""" transformed_from = [ correlation[1].context for correlation in self.get_related_contents( CorrelationType.solid, ['transformation']) if correlation[1].context ] return transformed_from[0] if transformed_from else None @property def is_expired(self): if 'closed' in self.state: return True deadline = getattr(self, 'deadline', None) if deadline is not None: now = datetime.datetime.now(tz=pytz.UTC) return now.date() >= deadline return False @property def can_add_content(self): return not self.is_expired and 'pending' in self.state @property def remaining_duration(self): deadline = getattr(self, 'deadline', None) duration = getattr(self, 'duration', None) if deadline is not None and duration is not None: now = datetime.datetime.now(tz=pytz.UTC) remaining = (deadline - now.date()).days return remaining if remaining >= 0 else 0 return None def init_published_at(self): setattr(self, 'published_at', datetime.datetime.now(tz=pytz.UTC)) def init_support_history(self): if not hasattr(self, '_support_history'): setattr(self, '_support_history', PersistentList()) def init_total_days(self): deadline = getattr(self, 'deadline', None) date = getattr(self, 'published_at', None) date = date if date else getattr(self, 'created_at', None) if deadline is not None and date is not None: duration = (deadline - date.date()).days setattr(self, 'duration', duration) def is_managed(self, root): return root.manage_challenges def get_attached_files_data(self): return get_files_data(self.attached_files) def get_all_attached_files_data(self): files = list(self.attached_files) files.append(self.image) return get_files_data(files) def get_node_descriminator(self): return 'challenge' def format(self, request): text = getattr(self, 'text', '') all_urls, url_files, text_urls, formatted_text = text_urls_format( text, request, True) self.urls = PersistentDict(all_urls) self.setproperty('url_files', url_files) self.formatted_text = formatted_text self.formatted_urls = text_urls
class WorkingGroup(VisualisableElement, Entity): """Working group class""" name = renamer() template = 'pontus:templates/visualisable_templates/object.pt' proposal = SharedUniqueProperty('proposal', 'working_group') members = SharedMultipleProperty('members', 'working_groups') wating_list = SharedMultipleProperty('wating_list') ballots = CompositeMultipleProperty('ballots') improvement_cycle_proc = SharedUniqueProperty('improvement_cycle_proc') workspace = CompositeUniqueProperty('workspace', 'working_group') def init_workspace(self): self.addtoproperty('workspace', Workspace(title="Workspace")) @property def work_mode(self): mode_id = getattr(self, 'work_mode_id', None) if mode_id: return WORK_MODES.get(mode_id, None) root = self.__parent__ if hasattr(root, 'get_work_modes') and len(root.get_work_modes()) == 1: return root.get_default_work_mode() return None @property def challenge(self): return getattr(self.proposal, 'challenge', None) def get_state(self, request, user): return get_states_mapping(user, self, getattr(self, 'state_or_none', [None])[0]) def empty(self, remove_author=True): author = self.proposal.author self.state = PersistentList(['deactivated']) self.setproperty('wating_list', []) if hasattr(self, 'first_improvement_cycle'): del self.first_improvement_cycle if hasattr(self, 'first_vote'): del self.first_vote members = self.members if remove_author and author in members: members.remove(author) for member in members: self.delfromproperty('members', member) revoke_roles(member, (('Participant', self.proposal), )) self.init_nonproductive_cycle() def inc_iteration(self): self.iteration = getattr(self, 'iteration', 0) + 1 def init_nonproductive_cycle(self): self.nonproductive_cycle = 0 def inc_nonproductive_cycle(self): self.nonproductive_cycle = getattr(self, 'nonproductive_cycle', 0) + 1 def is_member(self, user): mask = getattr(user, 'mask', None) return user in self.members or (mask and mask in self.members) def in_wating_list(self, user): mask = getattr(user, 'mask', None) return user in self.wating_list or (mask and mask in self.wating_list) def get_member(self, user): if not self.is_member(user): return None if user in self.members: return user mask = getattr(user, 'mask', None) if mask and mask in self.members: return mask return None def get_member_in_wating_list(self, user): if not self.in_wating_list(user): return None if user in self.wating_list: return user mask = getattr(user, 'mask', None) if mask and mask in self.wating_list: return mask return None
class DuplicableEntity(Entity): """ A Duplicable entity is an entity that can be duplicated""" originalentity = SharedUniqueProperty('originalentity', 'duplicates') duplicates = SharedMultipleProperty('duplicates', 'originalentity')
class Alert(VisualisableElement, Entity): """Alert class""" users_to_alert = SharedMultipleProperty('users_to_alert') subjects = SharedMultipleProperty('subjects') def __init__(self, kind, **kwargs): super(Alert, self).__init__(**kwargs) self.set_data(kwargs) self.kind = kind self.users_toalert = OOBTree() self.users_toexclude = OOBTree() @property def pattern(self): return INTERNAL_ALERTS.get(self.kind, None) @property def templates(self): return self.pattern.templates @property def icon(self): return self.pattern.get_icon(self) def init_alert(self, users, subjects=[], exclude=[]): self.subscribe(users) for subject in subjects: self.addtoproperty('subjects', subject) self.exclude(exclude) def subscribe(self, users): if not isinstance(users, (list, tuple, set)): users = [users] for user in users: oid = get_oid(user, user) self.users_toalert[str(oid)] = oid def unsubscribe(self, user): key = str(get_oid(user, user)) if key in self.users_toalert: self.users_toalert.pop(key) user.addtoproperty('old_alerts', self) self.reindex() def exclude(self, users): if not isinstance(users, (list, tuple, set)): users = [users] for user in users: oid = get_oid(user, user) self.users_toexclude[str(oid)] = oid def is_to_alert(self, user): key = str(get_oid(user, user)) # TODO self not in user.old_alerts return key in self.users_toalert and \ key not in self.users_toexclude def get_subject_state(self, subject, user, last_state=False): states = getattr(subject, 'state_or_none', [None]) state = states[0] if last_state: state = states[-1] return get_states_mapping( user, subject, state) def render(self, template, current_user, request): layout_manager = getattr(request, 'layout_manager', None) layout = layout_manager.layout if layout_manager \ else GlobalLayout(None, request) render_dict = { 'object': self, 'current_user': current_user, 'layout': layout } return renderers.render( self.templates[template], render_dict, request) def is_kind_of(self, kind): return kind == self.kind def has_args(self, **kwargs): for key in kwargs: if getattr(self, key, None) != kwargs[key]: return False return True
class Comment(Commentable, CorrelableEntity, Emojiable, SignalableEntity): """Comment class""" icon = 'icon ion-chatbubbles' templates = {'default': 'novaideo:views/templates/comment_result.pt'} name = renamer() author = SharedUniqueProperty('author') files = CompositeMultipleProperty('files') url_files = CompositeMultipleProperty('url_files') related_correlation = SharedUniqueProperty('related_correlation', 'targets') contextualized_correlations = SharedMultipleProperty( 'contextualized_correlations', 'context') def __init__(self, **kwargs): super(Comment, self).__init__(**kwargs) self.set_data(kwargs) self.urls = PersistentDict({}) self.edited = False self.pinned = False @property def channel(self): """Return the channel of th commented entity""" if not isinstance(self.__parent__, Comment): return self.__parent__ else: return self.__parent__.channel @property def root(self): """Return the root comment""" if not isinstance(self.__parent__, Comment): return self else: return self.__parent__.root @property def comment_parent(self): """Return the root comment""" if isinstance(self.__parent__, Comment): return self.__parent__ else: return None @property def subject(self): """Return the commented entity""" return self.channel.get_subject() @property def challenge(self): return getattr(self.subject, 'challenge', None) @property def relevant_data(self): return [ getattr(self, 'comment', ''), getattr(self.author, 'title', getattr(self.author, '__name__', '')) ] @property def related_contents(self): subject = self.subject return [ content[0] for content in self.contextualized_contents if content[0] is not subject ] @property def associated_contents(self): subject = self.subject return [ content[0] for content in self.contextualized_contents if content[0] is not subject and not getattr(content[1], 'tags', []) ] def set_associated_contents(self, associated_contents, user): subject = self.subject current_associated_contents = self.associated_contents associated_contents_to_add = [ i for i in associated_contents if i not in current_associated_contents ] associated_contents_to_del = [ i for i in current_associated_contents if i not in associated_contents and i not in associated_contents_to_add ] correlations = connect( subject, associated_contents_to_add, { 'comment': _('Add related contents'), 'type': _('Edit the comment') }, author=user, ) for correlation in correlations: correlation.setproperty('context', self) disconnect(subject, associated_contents_to_del) def get_title(self): return self.subject.title def presentation_text(self, nb_characters=400): return getattr(self, 'comment', "")[:nb_characters] + '...' def get_discuss_url(self, request, user): subject = self.channel.get_subject(user) return request.resource_url(subject, "@@index") + '#comment-' + str( get_oid(self, 'None')) def get_url(self, request): return request.resource_url(request.root, "@@seecomment", query={'comment_id': get_oid(self)}) def get_related_contents(self, user): return [r for r in self.related_contents if can_access(user, r)] def format(self, request, is_html=False): comment = getattr(self, 'comment', '') all_urls, url_files, text_urls, formatted_text = text_urls_format( comment, request, is_html) self.urls = PersistentDict(all_urls) self.setproperty('url_files', url_files) self.formatted_comment = formatted_text self.formatted_urls = text_urls def get_attached_files_data(self): return get_files_data(self.files) def can_add_reaction(self, user, process): return True
class SearchableEntity(ServiceableEntity): """ A Searchable entity is an entity that can be searched""" icon = 'glyphicon glyphicon-question-sign' templates = {'default': 'lac:views/templates/default_result.pt', 'bloc': 'lac:views/templates/default_result_bloc.pt', 'extraction': 'lac:views/templates/extraction/default_result.pt'} tree = synchronize_tree() type_title = '' files = CompositeMultipleProperty('files') connections_from = SharedMultipleProperty( 'connections_from', 'connections_to') connections_to = SharedMultipleProperty( 'connections_to', 'connections_from') visibility_dates = dates('visibility_dates') labels = SharedMultipleProperty('labels') def __init__(self, **kwargs): super(SearchableEntity, self).__init__(**kwargs) self._tree = PersistentDict() self.keywords = PersistentList() self.set_data(kwargs) self.source_site = get_oid(get_site_folder(True), None) self._keywords_ = [] self._init_keywords() def __setattr__(self, name, value): super(SearchableEntity, self).__setattr__(name, value) if name == 'description': self._init_presentation_text() @property def object_labels(self): return {'labels': self.labels} @property def metadata(self): return self.get_data(omit(MetadataSchema(), '_csrf_token_')) @property def is_published(self): return 'published' in self.state @property def object_id(self): source_data = getattr(self, 'source_data', {}) obj_id = source_data.get('id', '') + '_' +\ source_data.get('source_id', '') if obj_id == '_': obj_id = str(getattr(self, '__oid__', None))+'_lac' return obj_id @property def json_tree(self): return json.dumps(dict(self.tree)) @property def is_imported(self): source_id = getattr(self, 'source_data', {}).get('source_id', None) return True if source_id and source_id in list(IMPORT_SOURCES.keys())\ else False @property def relevant_data(self): return [getattr(self, 'title', ''), getattr(self, 'description', ''), ', '.join(self.keywords)] @property def sections(self): tree = dict(self.get_normalized_tree()) levels = get_keywords_by_level(tree, ROOT_TREE) if len(levels) >= 2: return sorted(levels[1]) return [] @property def substitutions(self): return [self] def _init_presentation_text(self): pass def _init_keywords(self): pass def get_all_keywords(self): return self.keywords def get_more_contents_criteria(self): "return specific query, filter values" keywords = list(self.keywords) root_tree = ROOT_TREE.lower() if root_tree in keywords: keywords.remove(root_tree) return None, {'keywords': keywords} def get_release_date(self): return getattr(self, 'release_date', self.modified_at) def presentation_text(self, nb_characters=400): return getattr(self, 'description', "")[:nb_characters]+'...' def get_normalized_tree(self, type_='out'): site = get_site_folder(True) return site.get_normalized_tree(getattr(self, 'tree', {}), type_) def get_visibility_filter(self): registry = get_current_registry() return { 'metadata_filter': { 'tree': deepcopy(self.tree), 'content_types': [registry.content.typeof(self)], 'states': list(getattr(self, 'state', [])) } } def set_metadata(self, appstruct): if 'site' not in appstruct: site = get_site_folder(True) if 'accessibility' in appstruct: object_set_access_control(appstruct.get('accessibility'), self, site) appstruct.pop('accessibility') data = {} for key in MetadataSchema(): name = key.name if name in appstruct: data[name] = appstruct.pop(name) if 'object_labels' in data: new_labels = [label['_object_data'] for label in data['object_labels']['new_labels']] all_labels = [] if new_labels: root = site.__parent__ for label in new_labels: root.addtoproperty('labels', label) all_labels.append(label) all_labels.extend(data['object_labels']['labels']) data['labels'] = all_labels data.pop('object_labels') self.set_data(data) self.release_date = getattr( self, 'release_date', self.created_at).replace( tzinfo=pytz.UTC) def labels_data(self, site): labels = getattr(self, 'labels', []) labels_data = [{'title': l.title, 'img': l.picture.url} for l in labels] site_oid = get_oid(site) if site_oid != self.source_site: orig_site = get_obj(self.source_site) if orig_site.favicon: labels_data.append({'title': orig_site.title, 'img': orig_site.favicon.url}) return labels_data
class DuplicableEntity(Entity): """ A Duplicable entity is an entity that can be duplicated""" original = SharedUniqueProperty('original', 'branches') branches = SharedMultipleProperty('branches', 'original')
class Person(User, SearchableEntity, CorrelableEntity, Debatable): """Person class""" type_title = _('Person') icon = 'icon glyphicon glyphicon-user' #'icon novaideo-icon icon-user' templates = { 'default': 'novaideo:views/templates/person_result.pt', 'bloc': 'novaideo:views/templates/person_bloc.pt', 'small': 'novaideo:views/templates/small_person_result.pt', 'popover': 'novaideo:views/templates/person_popover.pt', 'card': 'novaideo:views/templates/person_card.pt', 'header': 'novaideo:views/templates/person_header.pt', } default_picture = 'novaideo:static/images/user100.png' name = renamer() tokens = CompositeMultipleProperty('tokens') tokens_ref = SharedMultipleProperty('tokens_ref') organization = SharedUniqueProperty('organization', 'members') events = SharedMultipleProperty('events', 'author') picture = CompositeUniqueProperty('picture') cover_picture = CompositeUniqueProperty('cover_picture') ideas = SharedMultipleProperty('ideas', 'author') selections = SharedMultipleProperty('selections') working_groups = SharedMultipleProperty('working_groups', 'members') wg_participations = SharedMultipleProperty('wg_participations', 'wating_list_participation') old_alerts = SharedMultipleProperty('old_alerts') following_channels = SharedMultipleProperty('following_channels', 'members') folders = SharedMultipleProperty('folders', 'author') questions = SharedMultipleProperty('questions', 'author') challenges = SharedMultipleProperty('challenges', 'author') ballots = CompositeMultipleProperty('ballots') mask = SharedUniqueProperty('mask', 'member') def __init__(self, **kwargs): self.branches = PersistentList() self.keywords = PersistentList() super(Person, self).__init__(**kwargs) kwargs.pop('password', None) self.set_data(kwargs) self.set_title() self.last_connection = datetime.datetime.now(tz=pytz.UTC) self._read_at = OOBTree() self.guide_tour_data = PersistentDict({}) self.confidence_index = 0 self._notes = OOBTree() self.allocated_tokens = OOBTree() self.len_allocated_tokens = PersistentDict({}) self.reserved_tokens = PersistentList([]) self._submited_at = OOBTree() self._reported_at = OOBTree() def __setattr__(self, name, value): super(Person, self).__setattr__(name, value) if name == 'organization' and value: self.init_contents_organizations() def get_len_tokens(self, root=None, exclude_reserved_tokens=False): root = root or getSite() return root.tokens_mini if exclude_reserved_tokens \ else root.tokens_mini + len(self.reserved_tokens) def get_len_evaluations(self, exclude_reserved_tokens=False): total = self.len_allocated_tokens.get(Evaluations.support, 0) + \ self.len_allocated_tokens.get(Evaluations.oppose, 0) if exclude_reserved_tokens: return total - len([ o for o in self.reserved_tokens if o in self.allocated_tokens ]) return total def get_len_free_tokens(self, root=None, exclude_reserved_tokens=False): root = root or getSite() return self.get_len_tokens(root, exclude_reserved_tokens) - \ self.get_len_evaluations(exclude_reserved_tokens) def has_token(self, obj=None, root=None): root = root or getSite() obj_oid = get_oid(obj, None) if obj_oid and obj_oid in self.reserved_tokens: return obj_oid not in self.allocated_tokens return self.get_len_free_tokens(root, True) > 0 def add_token(self, obj, evaluation_type, root=None): if self.has_token(obj, root): self.allocated_tokens[get_oid(obj)] = evaluation_type self.len_allocated_tokens.setdefault(evaluation_type, 0) self.len_allocated_tokens[evaluation_type] += 1 def remove_token(self, obj): obj_oid = get_oid(obj) if obj_oid in self.allocated_tokens: evaluation_type = self.allocated_tokens.pop(obj_oid) self.len_allocated_tokens.setdefault(evaluation_type, 0) self.len_allocated_tokens[evaluation_type] -= 1 def add_reserved_token(self, obj): obj_oid = get_oid(obj) if obj_oid not in self.reserved_tokens: self.reserved_tokens.append(obj_oid) def remove_reserved_token(self, obj): obj_oid = get_oid(obj) if obj_oid in self.reserved_tokens: self.reserved_tokens.remove(obj_oid) def evaluated_objs(self, evaluation_type=None): if evaluation_type: return [ get_obj(key) for value, key in self.allocated_tokens.byValue( evaluation_type) ] return [get_obj(key) for key in self.allocated_tokens.keys()] def evaluated_objs_ids(self, evaluation_type=None): if evaluation_type: return [ key for value, key in self.allocated_tokens.byValue( evaluation_type) ] return list(self.allocated_tokens.keys()) def init_contents_organizations(self): novaideo_catalog = find_catalog('novaideo') dace_catalog = find_catalog('dace') organizations_index = novaideo_catalog['organizations'] object_authors_index = novaideo_catalog['object_authors'] object_provides_index = dace_catalog['object_provides'] query = object_authors_index.any([get_oid(self)]) & \ object_provides_index.any( [Iidea.__identifier__, IProposal.__identifier__]) & \ organizations_index.any([0]) for entity in query.execute().all(): entity.init_organization() entity.reindex() def set_read_date(self, channel, date): self._read_at[get_oid(channel)] = date def get_read_date(self, channel): return self._read_at.get(get_oid(channel), datetime.datetime.now(tz=pytz.UTC)) def get_channel(self, user): all_channels = list(self.channels) all_channels.extend(list(getattr(user, 'channels', []))) for channel in all_channels: if user in channel.members and self in channel.members: return channel return None def addtoproperty(self, name, value, moving=None): super(Person, self).addtoproperty(name, value, moving) if name == 'selections': value.len_selections = getattr(value, 'len_selections', 0) value.len_selections += 1 def delfromproperty(self, name, value, moving=None): super(Person, self).delfromproperty(name, value, moving) if name == 'selections': value.len_selections = getattr(value, 'len_selections', 0) if value.len_selections > 0: value.len_selections -= 1 def set_title(self): if getattr(self, 'pseudonym', ''): self.title = self.pseudonym else: self.title = getattr(self, 'first_name', '') + ' ' + \ getattr(self, 'last_name', '') def add_note(self, user, context, note, date, time_constant): self._notes[date] = (get_oid(user), get_oid(context), note) self.calculate_confidence_index(time_constant) def get_questions(self, user): if user is self: return self.questions + getattr(self.mask, 'questions', []) return self.questions def get_ideas(self, user): if user is self: return self.ideas + getattr(self.mask, 'ideas', []) return self.ideas def get_working_groups(self, user): if user is self: return self.working_groups + getattr(self.mask, 'working_groups', []) return self.working_groups @property def proposals(self): return [wg.proposal for wg in self.working_groups] def get_proposals(self, user): if user is self: return self.proposals + getattr(self.mask, 'proposals', []) return self.proposals @property def contacts(self): return [s for s in self.selections if isinstance(s, Person)] @property def participations(self): result = [ p for p in list(self.proposals) if any(s in p.state for s in [ 'amendable', 'open to a working group', 'votes for publishing', 'votes for amendments' ]) ] return result def get_participations(self, user): if user is self: return self.participations + getattr(self.mask, 'participations', []) return self.participations @property def contents(self): result = [i for i in list(self.ideas) if i is i.current_version] result.extend(self.proposals) result.extend(self.questions) result.extend(self.challenges) result.extend(self.events) return result def get_contents(self, user): if user is self: return self.contents + getattr(self.mask, 'contents', []) return self.contents @property def active_working_groups(self): return [p.working_group for p in self.participations] def get_active_working_groups(self, user): if user is self: return self.active_working_groups + getattr( self.mask, 'active_working_groups', []) return self.active_working_groups def get_wg_participations(self, user): if user is self: return self.wg_participations + getattr(self.mask, 'wg_participations', []) return self.wg_participations @property def is_published(self): return 'active' in self.state @property def managed_organization(self): return get_objects_with_role(user=self, role='OrganizationResponsible') def get_confidence_index(self): return getattr(self, 'confidence_index', 0) def reindex(self): super(Person, self).reindex() root = getSite() self.__access_keys__ = PersistentList(generate_access_keys(self, root)) def get_picture_url(self, kind, default): if self.picture: img = getattr(self.picture, kind, None) if img: return img.url return default def get_more_contents_criteria(self): "return specific query, filter values" return None, None def set_organization(self, organization): current_organization = self.organization if organization: if current_organization is not organization: is_manager = current_organization and has_role( ('OrganizationResponsible', current_organization), self, ignore_superiors=True) if current_organization and is_manager: revoke_roles( self, (('OrganizationResponsible', current_organization), )) self.setproperty('organization', organization) elif current_organization: is_manager = has_role( ('OrganizationResponsible', current_organization), self, ignore_superiors=True) if is_manager: revoke_roles( self, (('OrganizationResponsible', current_organization), )) self.delfromproperty('organization', current_organization) @property def all_alerts(self): novaideo_catalog = find_catalog('novaideo') dace_catalog = find_catalog('dace') alert_keys_index = novaideo_catalog['alert_keys'] alert_exclude_keys_index = novaideo_catalog['alert_exclude_keys'] object_provides_index = dace_catalog['object_provides'] exclude = [str(get_oid(self))] if self.mask: exclude.append(str(get_oid(self.mask))) query = object_provides_index.any([IAlert.__identifier__]) & \ alert_keys_index.any(self.get_alerts_keys()) & \ alert_exclude_keys_index.notany(exclude) return query.execute() @property def alerts(self): old_alerts = [get_oid(a) for a in self.old_alerts] result = self.all_alerts def exclude(result_set, docids): filtered_ids = list(result_set.ids) for _id in docids: if _id in docids and _id in filtered_ids: filtered_ids.remove(_id) return result_set.__class__(filtered_ids, len(filtered_ids), result_set.resolver) return exclude(result, old_alerts) def get_alerts_keys(self): result = ['all', str(get_oid(self))] if self.mask: result.append(str(get_oid(self.mask))) return result def get_alerts(self, alerts=None, kind=None, subject=None, **kwargs): if alerts is None: alerts = self.alerts if kind: alerts = [a for a in alerts if a.is_kind_of(kind)] if subject: alerts = [a for a in alerts if subject in a.subjects] if kwargs: alerts = [a for a in alerts if a.has_args(**kwargs)] return alerts def calculate_confidence_index(self, time_constant): now = datetime.datetime.utcnow().timestamp() notes = np.array([v[2] for v in self._notes.values()]) dates = np.array([int(t.timestamp()) for t in self._notes.keys()]) time_c = time_constant * 86400 confidence_index = np.sum( np.dot(notes, np.exp(-np.log(2) * (now - dates) / time_c))) self.confidence_index = round(confidence_index, 1) @property def user_groups(self): groups = list(self.groups) if self.organization: groups.append(self.organization) if self.mask: groups.append(self.mask) return groups @property def user_locale(self): locale = getattr(self, 'locale', None) if not locale: locale = getSite(self).locale return locale def _init_mask(self, root): if not self.mask: mask = Mask() root.addtoproperty('masks', mask) self.setproperty('mask', mask) def get_mask(self, root=None): root = root if root else getSite() if not getattr(root, 'anonymisation', False): return self self._init_mask(root) return self.mask def add_submission(self, obj): now = datetime.datetime.now(tz=pytz.UTC) self._submited_at[now] = get_oid(obj) def add_report(self, obj): now = datetime.datetime.now(tz=pytz.UTC) self._reported_at[now] = get_oid(obj) def can_submit_idea(self, root=None): root = root if root else getSite() now = datetime.datetime.now(tz=pytz.UTC) monday = datetime.datetime.combine((now - datetime.timedelta(days=7)), datetime.time(0, 0, 0, tzinfo=pytz.UTC)) return len(self._submited_at.values(min=monday, max=now)) < getattr( root, 'nb_submission_maxi', 3) def can_report(self, root=None): root = root if root else getSite() now = datetime.datetime.now(tz=pytz.UTC) monday = datetime.datetime.combine((now - datetime.timedelta(days=7)), datetime.time(0, 0, 0, tzinfo=pytz.UTC)) return len(self._reported_at.values(min=monday, max=now)) < getattr( root, 'nb_reports_maxi', 3)
class Ballot(VisualisableElement, Entity): """Ballot class""" name = renamer() ballot_box = CompositeUniqueProperty('ballot_box') report = CompositeUniqueProperty('report', 'ballot') initiator = SharedUniqueProperty('initiator') subjects = SharedMultipleProperty('subjects') def __init__(self, ballot_type, electors, contexts, duration, **kwargs): super(Ballot, self).__init__(**kwargs) kwargs.pop('subjects', None) self.setproperty('ballot_box', BallotBox()) self.setproperty('report', Report(ballot_type, electors, contexts, **kwargs)) self.run_at = None self.duration = duration self.finished_at = None self.period_validity = kwargs.get('period_validity', None) self.group = kwargs.get('group', DEFAULT_BALLOT_GROUP) self.uid = uuid.uuid4().hex self.__name__ = self.uid @property def group_id(self): group = getattr(self, 'group', DEFAULT_BALLOT_GROUP) return group.get('group_id', None) @property def is_finished(self): if 'finished' in self.state: return True now = datetime.datetime.now(tz=pytz.UTC) if now > self.finished_at: self.state.append('finished') return True return False @property def decision_is_valide(self): if 'expired' in self.state: return False if self.period_validity is None: return True now = datetime.datetime.now(tz=pytz.UTC) end_decision = self.finished_at + self.period_validity if now > end_decision: self.state.append('expired') return False return True def get_url(self, request): ballot_oid = get_oid(self, '') return request.resource_url( request.root, '@@seeballot', query={'id': ballot_oid}) \ if ballot_oid else None def finish_ballot(self): if 'finished' not in self.state: self.finished_at = datetime.datetime.now(tz=pytz.UTC) self.state = PersistentList(['finished']) def run_ballot(self, context=None): """Run the ballot""" self.run_at = datetime.datetime.now(tz=pytz.UTC) self.finished_at = self.run_at + self.duration return self.report.ballottype.run_ballot(context)
class NovaIdeoApplication(CorrelableEntity, Debatable, Application): """Nova-Ideo class (Root)""" name = renamer() preregistrations = CompositeMultipleProperty('preregistrations') challenges = CompositeMultipleProperty('challenges') working_groups = CompositeMultipleProperty('working_groups') proposals = CompositeMultipleProperty('proposals') organizations = CompositeMultipleProperty('organizations') invitations = CompositeMultipleProperty('invitations') ideas = CompositeMultipleProperty('ideas') questions = CompositeMultipleProperty('questions') correlations = CompositeMultipleProperty('correlations') files = CompositeMultipleProperty('files') alerts = CompositeMultipleProperty('alerts') picture = CompositeUniqueProperty('picture') homepage_picture = CompositeUniqueProperty('homepage_picture') favicon = CompositeUniqueProperty('favicon') theme = CompositeUniqueProperty('theme') proposal_template = CompositeUniqueProperty('proposal_template') advertisings = CompositeMultipleProperty('advertisings') news_letter_members = SharedMultipleProperty('news_letter_members') general_chanel = SharedUniqueProperty('general_chanel') newsletters = CompositeMultipleProperty('newsletters') smart_folders = CompositeMultipleProperty('smart_folders') connectors = CompositeMultipleProperty('connectors') masks = CompositeMultipleProperty('masks') def __init__(self, **kwargs): super(NovaIdeoApplication, self).__init__(**kwargs) self.keywords = PersistentList() self.initialization() def __setattr__(self, name, value): super(NovaIdeoApplication, self).__setattr__(name, value) if name == 'mail_templates' and value: result = {} for template in value: mail_id = template.get('mail_id') languages = template.get('languages', []) languages = {m['locale']: m for m in languages} result[mail_id] = { 'title': template.get('title'), 'languages': languages } self._mail_templates = PersistentDict(result) @property def mail_conf(self): return self.get_data( omit(MailTemplatesConfigurationSchema(), '_csrf_token_')) @property def work_conf(self): result = self.get_data( omit(WorkParamsConfigurationSchema(), '_csrf_token_')) return result @property def user_conf(self): return self.get_data( omit(UserParamsConfigurationSchema(), '_csrf_token_')) @property def keywords_conf(self): return self.get_data(omit(KeywordsConfSchema(), '_csrf_token_')) @property def ui_conf(self): return self.get_data( omit(UserInterfaceConfigurationSchema(), '_csrf_token_')) @property def homepage_conf(self): return self.get_data( omit(HomepageConfigurationSchema(), '_csrf_token_')) @property def other_conf(self): return self.get_data(omit(OtherSchema(), '_csrf_token_')) @property def notif_conf(self): return self.get_data( omit(NotificationConfigurationSchema(), '_csrf_token_')) def get_newsletters_for_registration(self): return [ nl for nl in self.newsletters if getattr(nl, 'propose_to_registration', True) ] def get_newsletters_automatic_registration(self): """Get newsletters with automatic registration""" return [ nl for nl in self.newsletters if getattr(nl, 'automatic_registration', True) ] def initialization(self): self.reset_default_values() self.deadlines = PersistentList([datetime.datetime.now(tz=pytz.UTC)]) self.work_modes = list(WORK_MODES.keys()) self.colors_mapping = PersistentDict(DEFAULT_COLORS) def init_channels(self): if not self.general_chanel: self.addtoproperty('channels', Channel(title=_("General"))) self.setproperty('general_chanel', self.channels[0]) def reset_default_values(self): self.participants_mini = 3 self.participants_maxi = 12 self.participations_maxi = 5 self.tokens_mini = 7 @property def moderate_proposals(self): return 'proposal' in getattr(self, 'content_to_moderate', []) @property def moderate_ideas(self): return 'idea' in getattr(self, 'content_to_moderate', []) @property def examine_proposals(self): return 'proposal' in getattr(self, 'content_to_examine', []) @property def examine_ideas(self): return 'idea' in getattr(self, 'content_to_examine', []) @property def support_proposals(self): return 'proposal' in getattr(self, 'content_to_support', []) @property def support_ideas(self): return 'idea' in getattr(self, 'content_to_support', []) @property def manage_challenges(self): return 'challenge' in getattr(self, 'content_to_manage', DEFAULT_CONTENT_TO_MANAGE) @property def manage_questions(self): return 'question' in getattr(self, 'content_to_manage', DEFAULT_CONTENT_TO_MANAGE) @property def manage_proposals(self): return 'proposal' in getattr(self, 'content_to_manage', DEFAULT_CONTENT_TO_MANAGE) @property def titles(self): return DEFAULT_TITLES @property def comment_intentions(self): return DEFAULT_COMMENT_INTENTIONS @property def correlation_intentions(self): return DEFAULT_CORRELATION_INTENTIONS @property def idea_intentions(self): return DEFAULT_IDEA_INTENTIONS @property def amendment_intentions(self): return DEFAULT_AMENDMENT_INTENTIONS @property def channel(self): return getattr(self, 'general_chanel', None) def get_nonproductive_cycle_nb(self): return getattr(self, 'nonproductive_cycle_nb', 2) def init_files(self): for information in DEFAULT_FILES: if not self.get(information['name'], None): info_file = FileEntity(title=information['title']) content = information.get('content', '') content_file = information.get('content_file', None) if content_file: content_path = os.path.join(os.path.dirname(__file__), 'static', 'default_files', content_file) if os.path.exists(content_path): content = open(content_path).read() info_file.text = content info_file.__name__ = information['name'] self.addtoproperty('files', info_file) info_file.state = PersistentList(['draft']) setattr(self, information['name'], info_file) def get_mail_template(self, id, locale=None): if locale is None: locale = self.locale mail = getattr(self, '_mail_templates', {}).get(id, None) if not mail: mail = DEFAULT_SITE_MAILS.get(id, None) template = mail.get('languages').get(locale, None) if not template: template = mail.get('languages').get(self.locale, None) return template def get_mail(self, id): for mail in getattr(self, 'mail_templates', {}): if mail.get('mail_id', None) == id: return mail template = DEFAULT_SITE_MAILS.get(id, None) if template: template = template.copy() template['mail_id'] = id template['languages'] = list(template['languages'].values()) return template def get_site_sender(self): registry = get_current_registry() default_sender = registry.settings['mail.default_sender'] return default_sender def get_work_modes(self): modes = getattr(self, 'work_modes', []) modes = {m: WORK_MODES[m] for m in modes if m in WORK_MODES} if modes: return modes return WORK_MODES def get_default_work_mode(self): modes = list(self.get_work_modes().values()) modes = sorted(modes, key=lambda e: e.order) return modes[0] def add_colors_mapping(self, keys): if not hasattr(self, 'colors_mapping'): self.colors_mapping = PersistentDict(DEFAULT_COLORS) new_keywords = [k for k in keys if k not in self.colors_mapping] colors = random_color(len(new_keywords)) for index, keyword in enumerate(new_keywords): self.colors_mapping[keyword] = {'color': colors[index]} def get_color(self, key): if key in getattr(self, 'colors_mapping', {}): return self.colors_mapping[key] self.add_colors_mapping([key]) return self.colors_mapping[key] def merge_keywords(self, newkeywords): current_keywords = list(self.keywords) current_keywords.extend(newkeywords) self.keywords = PersistentList(list(set(current_keywords))) def get_title(self, user=None): return getattr(self, 'title', '') def get_connectors(self, connector_id): return filter(lambda c: c.connector_id == connector_id, self.connectors)
class Proposal(VersionableEntity, SearchableEntity, DuplicableEntity, CorrelableEntity, PresentableEntity, ExaminableEntity, Node, Emojiable, SignalableEntity, Debatable, Tokenable): """Proposal class""" type_title = _('Proposal') icon = 'icon novaideo-icon icon-proposal' templates = {'default': 'novaideo:views/templates/proposal_result.pt', 'small': 'novaideo:views/templates/small_proposal_result.pt', 'popover': 'novaideo:views/templates/proposal_popover.pt', 'bloc': 'novaideo:views/templates/proposal_bloc.pt'} template = 'novaideo:views/templates/proposal_list_element.pt' name = renamer() author = SharedUniqueProperty('author') organization = SharedUniqueProperty('organization') working_group = SharedUniqueProperty('working_group', 'proposal') amendments = CompositeMultipleProperty('amendments', 'proposal') corrections = CompositeMultipleProperty('corrections', 'proposal') attached_files = SharedMultipleProperty('attached_files') challenge = SharedUniqueProperty('challenge', 'proposals') opinions_base = OPINIONS def __init__(self, **kwargs): super(Proposal, self).__init__(**kwargs) self.set_data(kwargs) self._amendments_counter = 1 self.addtoproperty('channels', Channel()) @property def related_ideas(self): return [idea[0] for idea in self.get_related_contents( CorrelationType.solid, ['related_ideas'])] @property def related_contents(self): return [content[0] for content in self.all_related_contents] @property def is_published(self): return 'published' in self.state @property def authors(self): authors = self.working_group.members author = self.author if author not in authors: authors.append(author) return authors @property def workspace(self): working_group = self.working_group if working_group: return working_group.workspace if self.current_version is not self: return getattr(self.current_version, 'workspace', None) return None def __setattr__(self, name, value): super(Proposal, self).__setattr__(name, value) if name == 'author': self.init_organization() def init_organization(self): if not self.organization: organization = getattr(self.author, 'organization', None) if organization: self.setproperty('organization', organization) def init_published_at(self): setattr(self, 'published_at', datetime.datetime.now(tz=pytz.UTC)) def init_examined_at(self): setattr(self, 'examined_at', datetime.datetime.now(tz=pytz.UTC)) def is_managed(self, root): return root.manage_proposals def get_more_contents_criteria(self): "return specific query, filter values" return None, { 'metadata_filter': { 'content_types': ['proposal', 'idea'], 'keywords': list(self.keywords) } } def get_attached_files_data(self): return get_files_data(self.attached_files) def set_related_ideas(self, relatedideas, user): current_related_ideas = self.related_ideas related_ideas_to_add = [i for i in relatedideas if i not in current_related_ideas] related_ideas_to_del = [i for i in current_related_ideas if i not in relatedideas and i not in related_ideas_to_add] connect(self, related_ideas_to_add, {'comment': _('Add related ideas'), 'type': _('Edit the proposal')}, user, ['related_proposals', 'related_ideas'], CorrelationType.solid, True) disconnect(self, related_ideas_to_del, 'related_ideas', CorrelationType.solid) def get_version(self, user, parent): old_version = self.version copy_of_proposal = copy( self, parent, omit=('len_selections', 'graph'), roles=True) if old_version: copy_of_proposal.setproperty('version', old_version) copy_of_proposal.init_graph() copy_of_proposal.state = PersistentList(['version', 'archived']) copy_of_proposal.setproperty('author', self.author) copy_of_proposal.setproperty('originalentity', self.originalentity) for amendment in self.amendments: copy_of_proposal.addtoproperty('amendments', amendment) for correction in self.corrections: copy_of_proposal.addtoproperty('corrections', correction) for file_ in self.attached_files: copy_of_proposal.addtoproperty('attached_files', file_) copy_of_proposal.set_related_ideas( self.related_ideas, user) copy_of_proposal.reindex() return copy_of_proposal def remove_tokens(self, force=False): evaluators = self.evaluators() for user in evaluators: user.remove_token(self) user.remove_reserved_token(self) if force: self.remove_token(user) def get_node_descriminator(self): return 'proposal'