class ExecutionContext(Object): createds = SharedMultipleProperty('createds', 'creator', False) involveds = SharedMultipleProperty('involveds', 'involvers', False) process = SharedUniqueProperty('process', 'execution_context', True) def __init__(self): super(ExecutionContext, self).__init__() self.parent = None self.sub_execution_contexts = PersistentList() self.properties_names = PersistentList() def _reindex(self): if self.process is not None: self.process.reindex() def root_execution_context(self): """Return the root execution context""" if self.parent is None: return self else: return self.parent.root_execution_context() def add_sub_execution_context(self, ec): """Add a sub execution context. A sub-execution context is associated to a sub-process""" if not (ec in self.sub_execution_contexts): self.sub_execution_contexts.append(ec) ec.parent = self def remove_sub_execution_context(self, ec): if ec in self.sub_execution_contexts: self.sub_execution_contexts.remove(ec) ec.parent = None def _sub_involveds(self): result = list(self.involveds) for sec in self.sub_execution_contexts: result.extend(sec._sub_involveds()) return set(result) def all_involveds(self): """Return all involved entities. The search includes sub-execution contexts""" root = self.root_execution_context() return root._sub_involveds() def _sub_createds(self): result = list(self.createds) for sec in self.sub_execution_contexts: result.extend(sec._sub_createds()) return set(result) def all_createds(self): """Return all created entities. The search includes sub-execution contexts""" root = self.root_execution_context() return root._sub_createds() @property def active_involveds(self): """Return all current relations of type 'involved'""" result = {} properties = dict(self.properties_names) for name in properties.keys(): relation_result = self.get_involved_collection(name) if relation_result: index_key = name + '_index' i = self.get_localdata(index_key) result[name] = { 'name': name, 'type': 'collection', 'assocition_kind': properties[name], 'index': i, 'is_current': True, 'entities': relation_result } continue relation_result = self.involved_entity(name) i = 0 if relation_result is not None: i = len(self.getproperty(name)) relation_result = [relation_result] else: continue result[name] = { 'name': name, 'type': 'element', 'assocition_kind': properties[name], 'index': i, 'is_current': True, 'entities': relation_result } return result def _sub_active_involveds(self): result = dict(self.active_involveds) for sec in self.sub_execution_contexts: sub_active = sec._sub_active_involveds() for key, value in sub_active.items(): if key in result: result[key]['entities'].extend(value['entities']) else: result[key] = value return result def all_active_involveds(self): """Return all current relations of type 'involved'. The search includes sub-execution contexts""" root = self.root_execution_context() return root._sub_active_involveds() @property def classified_involveds(self): """Return all archived relations of type 'involved'""" result = {} properties = dict(self.properties_names) for name in properties.keys(): index_key = name + '_index' if hasattr(self, index_key): index = self.get_localdata(index_key) + 1 for i in range(index)[1:]: prop_name = name + '_' + str(i) self._init_property(prop_name, self.dynamic_properties_def[prop_name]) result[prop_name] = { 'name': name, 'type': 'collection', 'assocition_kind': properties[name], 'index': i, 'is_current': (i == (index - 1)), 'entities': self.getproperty(prop_name) } else: result[name] = { 'name': name, 'type': 'element', 'assocition_kind': properties[name], 'index': -1, 'is_current': None, 'entities': self.involved_entities(name) } return result def _sub_classified_involveds(self): result = dict(self.classified_involveds) for sec in self.sub_execution_contexts: sub_classified = sec._sub_classified_involveds() for key, value in sub_classified.items(): if key in result: result[key]['entities'].extend(value['entities']) else: result[key] = value return result def all_classified_involveds(self): """Return all archived relations of type 'involved'. The search includes sub-execution contexts""" root = self.root_execution_context() return root._sub_classified_involveds() #entity def add_involved_entity(self, name, value, type='involved'): self.addtoproperty('involveds', value) if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) self.addtoproperty(name, value) else: self.properties_names.append((name, type)) self.dynamic_properties_def[name] = (SHARED_MULTIPLE, 'involvers', True) self._init_property(name, self.dynamic_properties_def[name]) self.addtoproperty(name, value) self._reindex() def remove_entity(self, name, value): if value in self.involveds: self.delfromproperty('involveds', value) if value in self.createds: self.delfromproperty('createds', value) if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) self.delfromproperty(name, value) self._reindex() def add_created_entity(self, name, value): self.addtoproperty('createds', value) self.add_involved_entity(name, value, 'created') # involved_entity start def involved_entity(self, name, index=-1): result = self.get_involved_entity(name, index) if result is not None: return result result = self.find_involved_entity(name, index) if result: return result[0] def get_involved_entity(self, name, index=-1): if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) result = self.getproperty(name) if result and index < len(result): return result[index] collection = self.get_involved_collection(name, index) if collection: return collection[0] return None def find_subinvolved_entity(self, name, index=-1): result = self.get_involved_entity(name, index) if result is not None: return [result] else: result = [] for sec in self.sub_execution_contexts: result.extend(sec.find_subinvolved_entity(name, index)) return result def find_involved_entity(self, name, index=-1): root = self.root_execution_context() return root.find_subinvolved_entity(name, index) # involved_entity end # involved_entities start def involved_entities(self, name=None): result = self.get_involved_entities(name) if result: return result result = self.find_involved_entities(name) return result def get_involved_entities(self, name=None): if name is None: return list(self.involveds) if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) result = self.getproperty(name) return result result = [] collections = self.get_involved_collections(name) for collection in collections: result.extend(collection) return result def find_subinvolved_entities(self, name=None): result = self.get_involved_entities(name) if result: return result else: for sec in self.sub_execution_contexts: result.extend(sec.find_subinvolved_entities(name)) return result def find_involved_entities(self, name=None): root = self.root_execution_context() return root.find_subinvolved_entities(name) # involved_entities end # created_entity start def created_entity(self, name, index=-1): result = self.get_created_entity(name, index) if result is not None: return result result = self.find_created_entity(name, index) if result: return result[0] return None def get_created_entity(self, name, index=-1): if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) result = [e for e in self.getproperty(name) if e in self.createds] if result: return result[index] collection = self.get_created_collection(name, index) if collection: return collection[0] return None def find_subcreated_entity(self, name, index=-1): result = self.get_created_entity(name, index) if result is not None: return [result] else: result = [] for sec in self.sub_execution_contexts: result.extend(sec.find_subcreated_entity(name, index)) return result def find_created_entity(self, name, index=-1): root = self.root_execution_context() return root.find_subcreated_entity(name, index) # created_entity end # created_entities start def created_entities(self, name=None): result = self.get_created_entities(name) if result: return result result = self.find_created_entities(name) return result def get_created_entities(self, name=None): if name is None: return list(self.createds) if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) result = [e for e in self.getproperty(name) if e in self.createds] return result return [] def find_created_entities(self, name=None): root = self.root_execution_context() result_created = root.all_createds() result = [e for e in root.find_involved_entities(name) \ if e in result_created] return result # created_entities end # has relation_entity start def has_relation(self, value, name=None): if self.has_localrelation(value, name): return True return self.has_globalrelation(value, name) def has_subrelation(self, value, name=None): if self.has_localrelation(value, name): return True for sec in self.sub_execution_contexts: if sec.has_subrelation(value, name): return True return False def has_globalrelation(self, value, name=None): root = self.root_execution_context() return root.has_subrelation(value, name) def has_localrelation(self, value, name=None): if name is None: return value in self.involveds entities = self.get_involved_entities(name) if entities and value in entities: return True return False # has relation_entity end #collections def add_involved_collection(self, name, values, type='involved'): prop_name = name index_key = name + '_index' if not hasattr(self, index_key): self.add_data(index_key, 0) index = self.get_localdata(index_key) + 1 self.add_data(index_key, index) name = name + '_' + str(index) for value in values: self.addtoproperty('involveds', value) if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) self.addtoproperty(name, value) else: self.properties_names.append((prop_name, type)) self.dynamic_properties_def[name] = (SHARED_MULTIPLE, 'involvers', True) self._init_property(name, self.dynamic_properties_def[name]) self.addtoproperty(name, value) self._reindex() def remove_collection(self, name, values): index_key = name + '_index' if not hasattr(self, index_key): return index = self.get_localdata(index_key) name = name + '_' + str(index) for value in values: self.remove_entity(name, value) def add_created_collection(self, name, values): for value in values: self.addtoproperty('createds', value) self.add_involved_collection(name, values, 'created') # involved_collection start def involved_collection(self, name, index=-1): result = self.get_involved_collection(name, index) if result: return result result = self.find_involved_collection(name, index) if result: return result[0] return [] def get_involved_collection(self, name, index=-1): index_key = name + '_index' if not hasattr(self, index_key): return [] if index == -1: index = self.get_localdata(index_key) elif index > self.get_localdata(index_key): return [] name = name + '_' + str(index) if name in self.dynamic_properties_def: self._init_property(name, self.dynamic_properties_def[name]) return self.getproperty(name) return [] def find_subinvolved_collection(self, name, index=-1): result = self.get_involved_collection(name, index) if result: return [result] else: for sec in self.sub_execution_contexts: result.extend(sec.find_subinvolved_collection(name, index)) return result def find_involved_collection(self, name, index=-1): root = self.root_execution_context() return root.find_subinvolved_collection(name, index) # involved_collection end # involved_collections start def involved_collections(self, name=None): result = self.get_involved_collections(name) if result: return result result = self.find_involved_collections(name) return result def get_involved_collections(self, name=None): if name is None: return list(self.involveds) index_key = name + '_index' result = [] if hasattr(self, index_key): for index in range(self.get_localdata(index_key)): result.append(self.get_involved_collection(name, (index + 1))) return result def find_subinvolved_collections(self, name=None): result = self.get_involved_collections(name) if result: return [result] else: result = [] for sec in self.sub_execution_contexts: result.extend(sec.find_subinvolved_collections(name)) return result def find_involved_collections(self, name=None): root = self.root_execution_context() return root.find_subinvolved_collections(name) # involved_collections end # created_collection start def created_collection(self, name, index=-1): result = self.get_created_collection(name, index) if result: return result result = self.find_created_collection(name, index) if result: return result[0] return [] def get_created_collection(self, name, index=-1): collections = self.get_involved_collection(name, index) if not collections: return [] result = [e for e in collections if e in self.createds] return result def find_subcreated_collection(self, name, index=-1): result = self.get_created_collection(name, index) if result: return [result] else: result = [] for sec in self.sub_execution_contexts: result.extend(sec.find_subcreated_collection(name, index)) return result def find_created_collection(self, name, index=-1): root = self.root_execution_context() return root.find_subcreated_collection(name, index) # created_collection end # created_collections start def created_collections(self, name=None): result = self.get_created_collections(name) if result: return result result = self.find_created_collections(name) return result def get_created_collections(self, name=None): if name is None: return list(self.createds) index_key = name + '_index' result = [] if hasattr(self, index_key): for index in range(self.get_localdata(index_key)): result.append(self.get_created_collection(name, (index + 1))) return result def find_subcreated_collections(self, name): result = self.get_created_collections(name) if result: return [result] else: result = [] for sec in self.sub_execution_contexts: result.extend(sec.find_subcreated_collections(name)) return result def find_created_collections(self, name=None): root = self.root_execution_context() return root.find_subcreated_collections(name) # created_collections end #Data def add_data(self, name, data): if not hasattr(self, name): setattr(self, name, PersistentList()) getattr(self, name).append(data) def get_data(self, name, index=-1): data = self.get_localdata(name, index) if data is not None: return data datas = self.find_data(name, index) return datas[0] def get_localdata(self, name, index=-1): if hasattr(self, name): datas = getattr(self, name) if index == -1 or index < len(datas): return getattr(self, name)[index] return None def find_subdata(self, name, index=-1): result = self.get_localdata(name, index) if result is not None: return [result] else: result = [] for sec in self.sub_execution_contexts: result.extend(sec.find_subdata(name, index)) return result def find_data(self, name, index=-1): root = self.root_execution_context() return root.find_subdata(name, index)
class Mask(Group): name = renamer() member = SharedUniqueProperty('member', 'mask') ideas = SharedMultipleProperty('ideas', 'author') working_groups = SharedMultipleProperty('working_groups', 'members') questions = SharedMultipleProperty('questions', 'author') challenges = SharedMultipleProperty('challenges', 'author') templates = { 'card': 'novaideo:views/templates/anonymous_card.pt', } default_picture = 'novaideo:static/images/anonymous100.png' is_anonymous = True def __init__(self, **kwargs): super(Mask, self).__init__(**kwargs) self.set_data(kwargs) @property def title(self): default_title = 'Anonymous' root = getattr(self, '__parent__', None) anonymisation_kind = getattr(root, 'anonymisation_kind', AnonymisationKinds.anonymity) if anonymisation_kind == AnonymisationKinds.anonymity: return default_title name = self.name return name if name else default_title @property def first_name(self): return 'Anonymous' @property def last_name(self): return 'Anonymous' @property def email(self): return getattr(self.member, 'email', '') @property def user_locale(self): return getattr(self.member, 'user_locale', '') @property def proposals(self): return [wg.proposal for wg in self.working_groups] @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 @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) return result @property def active_working_groups(self): return [p.working_group for p in self.participations] def reindex(self): super(Mask, self).reindex() if self.member: self.member.reindex()
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 Idea(VersionableEntity, DuplicableEntity, SearchableEntity, CorrelableEntity, PresentableEntity, ExaminableEntity, Node, Emojiable, SignalableEntity, Debatable, Tokenable): """Idea class""" type_title = _('Idea') icon = 'icon novaideo-icon icon-idea' templates = { 'default': 'novaideo:views/templates/idea_result.pt', 'bloc': 'novaideo:views/templates/idea_bloc.pt', 'small': 'novaideo:views/templates/small_idea_result.pt', 'popover': 'novaideo:views/templates/idea_popover.pt' } template = 'novaideo:views/templates/idea_list_element.pt' name = renamer() author = SharedUniqueProperty('author', 'ideas') organization = SharedUniqueProperty('organization') attached_files = CompositeMultipleProperty('attached_files') url_files = CompositeMultipleProperty('url_files') challenge = SharedUniqueProperty('challenge', 'ideas') opinions_base = OPINIONS def __init__(self, **kwargs): super(Idea, self).__init__(**kwargs) self.set_data(kwargs) self.addtoproperty('channels', Channel()) self.urls = PersistentDict({}) @property def is_workable(self): request = get_current_request() idea_to_examine = 'idea' in request.content_to_examine if idea_to_examine: return True if 'favorable' in self.state else False return True @property def related_proposals(self): """Return all proposals that uses this idea""" return [ proposal[0] for proposal in self.get_related_contents( CorrelationType.solid, ['related_proposals']) ] @property def related_contents(self): """Return all related contents""" return [content[0] for content in self.all_related_contents] @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 authors(self): return [self.author] @property def relevant_data(self): return [ getattr(self, 'title', ''), getattr(self, 'text', ''), ', '.join(self.keywords) ] def __setattr__(self, name, value): super(Idea, 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 presentation_text(self, nb_characters=400): return truncate_text(getattr(self, 'text', ""), nb_characters) 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 get_node_descriminator(self): return 'idea' def format(self, request): text = getattr(self, 'text', '') all_urls, url_files, text_urls, formatted_text = text_urls_format( text, request) self.urls = PersistentDict(all_urls) self.setproperty('url_files', url_files) self.formatted_text = formatted_text self.formatted_urls = text_urls
class Answer(CorrelableEntity, PresentableEntity, ExaminableEntity, Node, Emojiable, SignalableEntity, Sustainable, Debatable): """Answer class""" type_title = _('Answer') icon = 'glyphicon glyphicon-saved' templates = {'default': 'novaideo:views/templates/answer_result.pt', 'bloc': 'novaideo:views/templates/answer_bloc.pt', 'small': 'novaideo:views/templates/small_answer_result.pt', 'popover': 'novaideo:views/templates/answer_popover.pt'} template = 'novaideo:views/templates/answer_list_element.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') ballots = CompositeMultipleProperty('ballots') ballot_processes = SharedMultipleProperty('ballot_processes') question = SharedUniqueProperty('question', 'answers') def __init__(self, **kwargs): super(Answer, self).__init__(**kwargs) self.set_data(kwargs) self.addtoproperty('channels', Channel()) self.urls = PersistentDict({}) def init_title(self): self.title = 'Answer: {question} {date}'.format( question=getattr(self.question, 'title', ''), date=to_localized_time(self.created_at, translate=True)) @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', [])] @property def challenge(self): return getattr(self.question, 'challenge', None) 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) @property def relevant_data(self): return [getattr(self, 'comment', ''), getattr(self, 'title', ''), getattr(self.author, 'title', getattr(self.author, '__name__', ''))] @property def subject(self): return self def presentation_text(self, nb_characters=400): return truncate_text(getattr(self, 'comment', ''), nb_characters) def get_related_contents(self, user): return [r for r in self.related_contents if can_access(user, r)] def get_attached_files_data(self): return get_files_data(self.files) def get_node_descriminator(self): return 'answer' def format(self, request): comment = getattr(self, 'comment', '') all_urls, url_files, text_urls, formatted_text = text_urls_format( comment, request) self.urls = PersistentDict(all_urls) self.setproperty('url_files', url_files) self.formatted_comment = formatted_text self.formatted_urls = text_urls
class Question(VersionableEntity, DuplicableEntity, SearchableEntity, CorrelableEntity, PresentableEntity, ExaminableEntity, Node, Emojiable, SignalableEntity, Sustainable, Debatable): """Question class""" type_title = _('Question') icon = 'md md-live-help' templates = {'default': 'novaideo:views/templates/question_result.pt', 'bloc': 'novaideo:views/templates/question_bloc.pt', 'small': 'novaideo:views/templates/small_question_result.pt', 'popover': 'novaideo:views/templates/question_popover.pt'} template = 'novaideo:views/templates/question_list_element.pt' name = renamer() author = SharedUniqueProperty('author', 'questions') organization = SharedUniqueProperty('organization') attached_files = CompositeMultipleProperty('attached_files') url_files = CompositeMultipleProperty('url_files') related_correlation = SharedUniqueProperty( 'related_correlation', 'targets') answers = CompositeMultipleProperty('answers', 'question') answer = SharedUniqueProperty('answer') ballots = CompositeMultipleProperty('ballots') ballot_processes = SharedMultipleProperty('ballot_processes') challenge = SharedUniqueProperty('challenge', 'questions') def __init__(self, **kwargs): super(Question, self).__init__(**kwargs) self.set_data(kwargs) self.addtoproperty('channels', Channel()) self.selected_options = OOBTree() self.users_options = OOBTree() self.urls = PersistentDict({}) self.len_answers = 0 @property def authors(self): return [self.author] @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 relevant_data(self): return [getattr(self, 'title', ''), getattr(self, 'text', ''), ', '.join(self.keywords)] def __setattr__(self, name, value): super(Question, self).__setattr__(name, value) if name == 'author': self.init_organization() def is_managed(self, root): return root.manage_questions def update_len_answers(self): self.len_answers = len(self.answers) return self.len_answers def addtoproperty(self, name, value, moving=None): super(Question, self).addtoproperty(name, value, moving) if name == 'answers': self.len_answers += 1 def delfromproperty(self, name, value, moving=None): super(Question, self).delfromproperty(name, value, moving) if name == 'answers': self.len_answers -= 1 def init_organization(self): if not self.organization: organization = getattr(self.author, 'organization', None) if organization: self.setproperty('organization', organization) def presentation_text(self, nb_characters=400): return truncate_text(getattr(self, 'text', ''), nb_characters) def get_more_contents_criteria(self): "return specific query, filter values" return None, { 'metadata_filter': { 'content_types': ['question'], 'keywords': list(self.keywords) } } def get_attached_files_data(self): return get_files_data(self.attached_files) def get_node_descriminator(self): return 'question' def format(self, request): text = getattr(self, 'text', '') all_urls, url_files, text_urls, formatted_text = text_urls_format( text, request) self.urls = PersistentDict(all_urls) self.setproperty('url_files', url_files) self.formatted_text = formatted_text self.formatted_urls = text_urls def add_selected_option(self, user, option): self.remove_selected_option(user) oid = get_oid(user) self.selected_options[oid] = option self.users_options.setdefault(option, []) if option in self.users_options: self.users_options[option].append(oid) else: self.users_options[option] = PersistentList([oid]) def remove_selected_option(self, user): oid = get_oid(user) if oid in self.selected_options: option = self.selected_options.pop(oid) if oid in self.users_options[option]: user_options = self.users_options[option] user_options.remove(oid) self.users_options[option] = PersistentList( list(set(user_options))) def get_selected_option(self, user): oid = get_oid(user) if oid in self.selected_options: return self.selected_options.get(oid) return None def get_user_with_option(self, option): options = getattr(self, 'options', []) if options and option in self.users_options: return self.users_options[option] return []